home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 June: Reference Library / Dev.CD Jun 00 RL Disk 1.toast / mac / What's New / • What was new 04⁄00 / Development Kits / Hardware / Mac OS USB DDK 1.4.1f4 / Examples / USBSampleStorageDriver / StorageClassUTFunctions.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-12-15  |  83.8 KB  |  2,746 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        StorageClassUTFunctions.c
  3.  
  4.     Contains:    All device specific functions
  5.  
  6.     Version:    1.1
  7.  
  8.     Copyright:    © 1998-1999 by Apple Computer, Inc., all rights reserved.
  9.  
  10. */
  11.  
  12. #include <Disks.h>
  13. #include <DriverGestalt.h>
  14. #include <Gestalt.h>
  15. #include <NameRegistry.h>
  16. #include <Power.h>
  17. #include <Resources.h>
  18. #include <Strings.h>
  19.  
  20. #include "SampleStorageDriverAPI.h"
  21. #include "SampleStorageVersion.h"
  22. #include "StorageClassUTFunctions.h"
  23. #include "StorageClassUTDriver.h"
  24. #include "StorageClassUTDriverIcons.h"
  25.  
  26. enum 
  27. {
  28.     kReadWriteRetryCount = 1,
  29.     kFormatRequestSenseRetryCount = 5
  30. };
  31.     
  32. enum {
  33. //    kCDBSize = 12,
  34.     kSenseDataSize = 18
  35. };
  36.  
  37.  
  38. enum {
  39.     kCmdFormat                     = 0x04,
  40.     kCmdInquiry                 = 0x12,
  41.     kCmdModeSelect                 = 0x55,
  42.     kCmdModeSense                 = 0x5A,
  43.     kCmdPreventAllowRemoval     = 0x1E,
  44.     kCmdRead                     = 0x28,
  45.     kCmdReadCapacity             = 0x25,
  46.     kCmdReadFormatCapacities     = 0x23,
  47.     kCmdRequestSense             = 0x03,
  48.     kCmdRezero                     = 0x01,
  49.     kCmdSeek                     = 0x2B,
  50.     kCmdSendDiagnostic             = 0x1D,
  51.     kCmdStartStopUnit             = 0x1B,
  52.     kCmdTestUnitReady             = 0x00,
  53.     kCmdWrite                     = 0x2A,
  54.     kCmdWriteVerify             = 0x2E,
  55.     kCmdVerify                     = 0x2F
  56. };
  57.  
  58. // The structure for getting the media capacity from the device    
  59. struct ReadCapacityData 
  60. {
  61.     UInt32        lastLogicalBlock;        // Last valid LBA
  62.     UInt32        blockLength;            // Block length in bytes
  63. };
  64. typedef struct ReadCapacityData     ReadCapacityData, *ReadCapacityDataPtr;
  65.  
  66. // This structure defines each volume (partition) on a physical drive (media)
  67. struct VolumeRec                        // This is needed to support multiple partitions
  68. {
  69.     DrvSts2            driveStatus;        // drive status info & queue element
  70.     UInt16            vRefNum;            // drive number for this volume
  71.     Boolean            mountthispart;        // mount this volume indicator
  72.     Boolean            partmounted;        // volume mounted indicator
  73.     UInt32            partitionNo;        // the partition number for the volume
  74.     UInt32            partoffset;            // phys. offset of data partition
  75.     UInt32            curoffset;            // 0 = physical mapping, else 'partoffset'
  76.     UInt32            partblks;            // number of blocks in the partition
  77.     Ptr                nextVol;            // Link to next volume in the list
  78.     Ptr                drivePtr;            // Pointer to the owning drive's record
  79.     Ptr                mediaIconPtr;        // Pointer to the media icon for this volume
  80. };
  81. typedef    struct VolumeRec     VolumeRec, *VolumeRecPtr;
  82.  
  83. // This structure defines each physical drive…
  84. struct DriveRec
  85. {
  86.     UInt32            capacity;            // Drive capacity in blocks
  87.     UInt32            blockSize;
  88.     UInt16            numVolumes;            // Number of partitions in the drive
  89.     VolumeRecPtr    nextVol;            // Pointer to drive's volume list    
  90. };
  91. typedef struct DriveRec     DriveRec, *DriveRecPtr;
  92.  
  93. // Our Sleep Queue structure.  The driver installs a sleep queue proc to find
  94. // out when the machine goes into sleep or doze mode.  When a sleep or doze
  95. // notification is received, the driver will stop polling for media until a wake
  96. // notification is received.    
  97. struct OurSleepQRec
  98. {
  99.     SleepQRec    theSleepQRec;
  100.     Boolean        isInSleep;
  101. };
  102. typedef struct OurSleepQRec    OurSleepQRec, *OurSleepQRecPtr;
  103.  
  104. // The Drive Request PB structure contains all information need to perform
  105. // a device request
  106. struct DriveRequestPB
  107. {
  108.     StorageExecuteCommandPB        executePB;
  109.     ParmBlkPtr                    theIOPB;
  110.     IOCommandID                 ioCommandID;
  111.     IOCommandKind                 ioCommandKind;
  112.     volatile OSStatus            status;
  113.     UInt16                        retryCount;
  114.     Boolean                        doWrite;
  115. };
  116. typedef struct DriveRequestPB    DriveRequestPB, *DriveRequestPBPtr;
  117.  
  118. // The driver's global data structure
  119. struct UTDriverGlobals
  120. {
  121.     DriveRequestPB                drivePB;        
  122.  
  123.     // Global Control/Status fields for the driver
  124.     Boolean                        isFloppy;
  125.     Boolean                        isWriteProtected;
  126.     Boolean                        doInternalReadWrite;
  127.     Boolean                        diskInDrive;
  128.     UInt8                        currentExecutionState;
  129.     
  130.     SInt16                        drvrRefNum;            // Our driver reference number
  131.     ReadCapacityData            getCapacity;
  132.     UInt8                        sense[40];
  133.     DriveRec                    theDrive;
  134.     VolumeRec                    theVolume;
  135.     OurSleepQRec                theSleepQRec;
  136.     Str32                        DriveInfoString;
  137. };
  138. typedef struct UTDriverGlobals    UTDriverGlobals;
  139.  
  140. // The values for the states of the state machines
  141. enum
  142. {
  143.     // Mount State Machine
  144.     kMountStartState = 1,
  145.     kMountTURDoneState,
  146.     kMountRequestSenseDoneState,
  147.     kMountGetGeometryDoneState,
  148.     kMountCheckGetGeometryErrorDone,
  149.     kMountReadPossibleCapacitiesDone,
  150.     kMountCheckWriteProtectDoneState,
  151.     kMountPreventRemovalDoneState,
  152.  
  153.     // Eject State Machine
  154.     kEjectStartState,
  155.     kEjectAllowRemovalDone,
  156.     kEjectCartridgeDone,
  157.  
  158.     // Format State Machine
  159.     kFormatStartState,
  160.     kFormatDoneState,
  161.     kFormatWaitDoneState,
  162.     kFormatRequestSenseDoneState,
  163.     kFormatGetGeometryDoneState,
  164.     kFormatCheckWriteProtectDoneState,
  165.     kFormatPreventRemovalDoneState
  166. };
  167.  
  168. #if UNIVERSAL_INTERFACES_VERSION < 0x0330
  169. /* Driver Control/Status Codes for Startup Disk Control Panel */
  170. enum
  171. {
  172.     kcsSetBootPartitionCode            = 44,        // Set startup partition Control Call
  173.     kcsGetBootPartitionStatus        = 44        // Get startup partition Status Call
  174. };
  175. #endif
  176.  
  177. // Prototypes for the Sleep Queue related functions
  178. static void             InstallInSleepQueue(OurSleepQRecPtr ourSleepQRec);    
  179. static void             RemoveFromSleepQueue(OurSleepQRecPtr ourSleepQRec);    
  180. static long             SleepNotification(long message, SleepQRecPtr sleepRec);
  181.  
  182. // Prototypes for the cartridge mounting related functions
  183. static OSStatus         MountSecondaryInterrupt( void *p1, void *p2);
  184. static void             MountTheCartridge( void *theCurrentPB );
  185.  
  186. // Prototypes for the cartridge ejecting related functions
  187. static void             EjectTheCartridge( void *theCurrentPB );
  188.  
  189. // Prototypes for the floppy formatting related functions
  190. static OSStatus         FormatCompletionInterrupt( void *p1, void *p2);
  191. static void             FormatTheCartridge( void *theCurrentPB );
  192.  
  193. // ATAPI/SCSI-2 Device Commands
  194. static OSStatus         CheckWriteProtect(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  195. static OSStatus         TUR(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  196. static OSStatus         GetMediaGeometry(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  197. static OSStatus         ReadFormatCapacity(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  198. static OSStatus            RequestSense(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  199. static OSStatus         FormatFloppyCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  200. static OSStatus         EjectCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  201. static OSStatus         PreventAllowRemoval(UTDriverGlobals *theCurrentPB, Boolean preventRemoval, StorageClassCompletionProcPtr ourCompletion);
  202.  
  203. // Prototypes for the Read/Write functions and completion routines
  204. static OSStatus         DoReadWriteCommand( UTDriverGlobals *theDriverPB, Boolean doWrite);
  205. static OSStatus         ReadWriteBlock( UTDriverGlobals *theDriverPB, UInt32 startBlock, UInt32 numBlocks, Ptr buffer, Boolean doWrite, Boolean doAsync);
  206. static void             ReadWriteCompletion( void *theDriverPB );
  207. static void             WriteRequestSenseCompletion( void *theDriverPB );
  208. static void             RequestSenseOnErrorCompletion( void *theDriverPB );
  209.  
  210. // Prototypes for the Drive Queue and Volume Queue related functions
  211. static void             InstallVolumes(DriveRecPtr theDrive, Boolean mountVols);
  212. static void                RemoveVolume(DriveRecPtr drvRec, UInt16 volRef);
  213. static VolumeRecPtr        GetVolume(DriveRecPtr drive, UInt16 vRefNum, UInt32 partitionNum);
  214. static VCB*                MountedVolOfDrive(DriveRecPtr drive);
  215. static SInt16            NextQDrive();
  216. static VolumeRecPtr        CreateVolume(DriveRecPtr drive, UInt32 partitionID, UInt32 volSize, UInt32 volOffset);
  217. static void             RemoveDrive(DriveRecPtr theDrivePtr);
  218. static void                UpdateQ(SInt16 qDrive, SInt32 newSize);
  219. static SInt32            NextPartitionID(DriveRecPtr drive);
  220. static Boolean             InstallDrive(DriveRecPtr drive, Boolean mountVols);
  221. static OSErr             MountVolumes( DriveRecPtr drive );
  222.  
  223. // This is to workaround a bug in the PowerPC native version of the AddDrive
  224. // call in systems before 8.5, where one needs to be added to the desired
  225. // drive number before calling AddDrive.
  226. // This function will check the system and pass the appropriate value to AddDrive
  227. static void             NativeAddDrive(SInt16 drvrRefNum, UInt16 driveNumber, DrvQElPtr drvQEl);
  228.  
  229. //----------------------------------------------------------------------------------
  230. //                                    Globals
  231. //----------------------------------------------------------------------------------
  232.  
  233. static DriverLocationIcon                gDriveIcon;                            // static structure for control calls
  234. static StorageClassDispatchTablePtr        gItsTheDispatchTable = nil;            // The class's dispatch table
  235. static TimerID                            gInterruptTimer = 0;
  236. static UTDriverGlobals                    gTheUTGlobals;
  237.  
  238.  
  239. //----------------------------------------------------------------------------------
  240. //                                    Driver calls
  241. //----------------------------------------------------------------------------------
  242.  
  243. //    Always run at task level, can allocate and move memory.
  244. OSStatus DriverInitializeCmd (    AddressSpaceID        addressSpaceID,
  245.                                 DriverInitInfoPtr    initialInfo)
  246. {
  247. #pragma unused (addressSpaceID, initialInfo)
  248.     
  249.     OSStatus    err = noErr;
  250.  
  251.     IfDebugging("\pInitialize Driver");    
  252.     gTheUTGlobals.drvrRefNum = initialInfo->refNum;
  253.     gTheUTGlobals.drivePB.theIOPB = nil;
  254.     gTheUTGlobals.currentExecutionState = kMountStartState;
  255.     gTheUTGlobals.doInternalReadWrite = false;
  256.     gTheUTGlobals.diskInDrive = false;
  257.     
  258.     gTheUTGlobals.isFloppy = false;
  259.  
  260.     // Set up our color icon family
  261.     BuildMediaIconFamily();
  262.  
  263.     // Clear out the Drive Record
  264.     BlockZero((Ptr) &gTheUTGlobals.theDrive, sizeof(DriveRec));
  265.     
  266.     // Clear out the Volume Record
  267.     BlockZero((Ptr) &gTheUTGlobals.theVolume, sizeof(VolumeRec));
  268.     
  269.     // Build the Drive Info string returned in Status/Control calls
  270.     {
  271.         Str32 tempStr;
  272.         
  273.         PStrCopy( gTheUTGlobals.DriveInfoString, "\pUSB (v");
  274.         CStrToPStr(tempStr, kStorageStringVersShort); // driver version
  275.         PStrCat(gTheUTGlobals.DriveInfoString, tempStr); // driver version
  276.         PStrCat(gTheUTGlobals.DriveInfoString, "\p)");
  277.     }
  278.  
  279.     BlockZero((Ptr) &gTheUTGlobals.theSleepQRec, sizeof(OurSleepQRec));
  280.     InstallInSleepQueue( &gTheUTGlobals.theSleepQRec );
  281.     return (err);
  282. }
  283.  
  284. //    Always run at task level, can allocate and move memory.
  285. OSStatus DriverFinalizeCmd (DriverFinalInfoPtr finalInfo)
  286. {
  287. #pragma unused (finalInfo)
  288.     OSStatus    err = noErr;
  289.  
  290.     DestroyMediaIconFamily();
  291.     RemoveFromSleepQueue( &gTheUTGlobals.theSleepQRec );    
  292.     
  293.     return (err);
  294. }
  295.  
  296. //    Always run at task level, can allocate and move memory.
  297. OSStatus DriverSupersededCmd (DriverSupersededInfoPtr supersededInfo)
  298. {
  299. #pragma unused (supersededInfo)
  300.  
  301.     OSErr    err = noErr;
  302.  
  303.     return (err);
  304. }
  305.  
  306. //    Always run at task level, can allocate and move memory.
  307. OSStatus DriverReplaceCmd (    AddressSpaceID            addressSpaceID,
  308.                             DriverReplaceInfoPtr    replaceInfo)
  309. {
  310. #pragma unused (addressSpaceID, replaceInfo)
  311.     OSStatus    err = noErr;
  312.  
  313.     return (err);
  314. }
  315.  
  316. //    Always run at task level, can allocate and move memory.
  317. OSStatus DriverOpenCmd ( AddressSpaceID    addressSpaceID, ParmBlkPtr pb)
  318. {
  319. #pragma unused (addressSpaceID, pb)
  320.     OSStatus    err         = noErr;
  321.  
  322.     // Let the world know we can do DriverGestalt calls
  323.     DriverGestaltOn(gTheUTGlobals.drvrRefNum);
  324.     
  325.     return(err);
  326. }
  327.  
  328. //    Always run at task level, can allocate and move memory.
  329. OSStatus DriverCloseCmd (ParmBlkPtr pb)
  330. {
  331. #pragma unused (pb)
  332.     OSStatus        err = noErr;
  333.  
  334.     gTheUTGlobals.diskInDrive = false;
  335.  
  336.     // If an interrupt timer is set, cancel it!
  337.     if( gInterruptTimer != 0 )
  338.     {
  339.         AbsoluteTime    timeLeft;
  340.         
  341.         // Cancel any pending timers
  342.         CancelTimer( gInterruptTimer, &timeLeft);
  343.         gInterruptTimer = 0;
  344.     }
  345.  
  346.     // If a command is pending, we should cancel it.
  347.     // Since we currently have no way of informing the Class driver to abort,
  348.     // we will wait for the class driver to tell us the command has finished.
  349.     while(    gTheUTGlobals.drivePB.theIOPB != nil );
  350.     
  351.     // Dequeue all drive volumes 
  352.     RemoveDrive(&gTheUTGlobals.theDrive);
  353.     
  354.     if(gItsTheDispatchTable != nil)
  355.     {
  356.         (void) ((gItsTheDispatchTable)->pStorageControl)(kControlEnableRemoval, nil);
  357.     }
  358.     
  359.     // Let the world know we can no longer doDriverGestalt calls
  360.     DriverGestaltOff(gTheUTGlobals.drvrRefNum);
  361.     
  362.     return (err);
  363. }
  364.  
  365. //    May run at interrupt level, CANNOT allocate or move memory.
  366. OSStatus DriverControlCmd (    AddressSpaceID    addressSpaceID,
  367.                             IOCommandID        ioCommandID,
  368.                             IOCommandKind    ioCommandKind,
  369.                             ParmBlkPtr        pb)
  370. {
  371. #pragma unused ( addressSpaceID )
  372.     OSStatus        err            = noErr;
  373.     CntrlParamPtr    pbPtr;
  374.     DriveRecPtr        drive         = &gTheUTGlobals.theDrive;
  375.  
  376.     pbPtr = (CntrlParamPtr) pb;
  377.     
  378.  
  379.     // Check if the control call is one that needs to use the parameter block
  380.     // if so, do the initialization on it.  There should be a check to make sure that
  381.     // the PB is not in use before it is initialized.
  382.     // This method will probably work because we shouldn't be getting these types of requests
  383.     // while reads and writes are happening, but this is not guaranteed
  384.     if ( (pbPtr->csCode == kVerify) || (pbPtr->csCode == kEject) || (pbPtr->csCode == kFormat)
  385.         || ( pbPtr->csCode == 21315 ))
  386.     {
  387.         // Should be a check here to make sure that the parameter block is not in use
  388.         BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  389.         
  390.         gTheUTGlobals.drivePB.theIOPB            = nil;
  391.         gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  392.         gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  393.         gTheUTGlobals.drivePB.retryCount         = 0;
  394.         gTheUTGlobals.drivePB.doWrite             = false;
  395.     }    
  396.  
  397.     // Parse the control codes…
  398.     switch(pbPtr->csCode) 
  399.     {
  400.         case killCode:
  401.         {
  402.             // This driver does not support the killCode,
  403.             // return back -1 as per TechNote DV 17: Sony Driver
  404.             err = -1;
  405.         }
  406.         break;
  407.         
  408.         case kVerify:                        // Verify the media, this should only be called for floppies
  409.         {
  410.             if(gTheUTGlobals.theDrive.capacity == 0)
  411.             {
  412.                 err = nsDrvErr;            // no Media is inserted, return an error
  413.             }
  414.             else if( gTheUTGlobals.isFloppy == true )
  415.             {
  416.                 UInt16     diskCapacity;
  417.                 UInt16     startBlock;
  418.                 UInt16    numberBlocks = 16;
  419.                 UInt8    *blockBuffer;
  420.                 
  421.                 diskCapacity = gTheUTGlobals.theDrive.capacity;
  422.                 blockBuffer = (UInt8 *) NewPtrSysClear(numberBlocks * gTheUTGlobals.theDrive.blockSize);
  423.                 if(blockBuffer == nil)
  424.                 {
  425.                     err = verErr;
  426.                 }
  427.                 else
  428.                 {
  429.                     for (startBlock = 0; startBlock<diskCapacity; startBlock+=numberBlocks)
  430.                     {
  431.                         gTheUTGlobals.doInternalReadWrite = true;
  432.                         err = ReadWriteBlock( &gTheUTGlobals, startBlock, numberBlocks,(Ptr)  blockBuffer, false, false);
  433.                         gTheUTGlobals.doInternalReadWrite = false;
  434.     
  435.                         // an Error has occured                    
  436.                         if ( err != noErr )
  437.                         {
  438.                             // report a verify error
  439.                             err = verErr;
  440.                             break;
  441.                         }
  442.                     }
  443.                     DisposePtr((Ptr) blockBuffer);
  444.                 }
  445.             }
  446.             else
  447.             {
  448.                 err = noErr;            // For the regular cartridge, just report noErr
  449.             }
  450.         }
  451.         break;
  452.  
  453.         case kFormat:
  454.         {
  455.             if (gTheUTGlobals.isWriteProtected == true)
  456.             {
  457.                 // check for write protect
  458.                 err = wPrErr;
  459.                 break;
  460.             }
  461.             
  462.             if(gTheUTGlobals.theDrive.capacity == 0)
  463.             {
  464.                 err = nsDrvErr;            // no Media is inserted, return an error
  465.             }
  466.             else if( gTheUTGlobals.isFloppy == true )
  467.             {
  468.                 gTheUTGlobals.currentExecutionState = kFormatStartState;
  469.                 FormatTheCartridge( &gTheUTGlobals );
  470.                 err = gTheUTGlobals.drivePB.status;
  471.             }
  472.             else
  473.             {
  474.                 err = noErr;            // For the regular cartridge, just report noErr
  475.             }
  476.         }
  477.         break;
  478.  
  479.         case kEject:
  480.         {
  481.             // We get this call whenever a volume is put away (dragged to trash).  
  482.             Boolean hasMountedVolume = (MountedVolOfDrive(drive) != nil);
  483.  
  484.             if (!hasMountedVolume)                    // If no more mounted volumes…
  485.             {
  486.                 gTheUTGlobals.currentExecutionState = kEjectStartState;
  487.                  EjectTheCartridge( &gTheUTGlobals );
  488.             }
  489.             
  490.         }
  491.         break;
  492.  
  493.         case kSetTagBuffer:                    // This is a floppy specific control call
  494.         {            
  495.             err = controlErr;                // Return a controlErr, since we do not support this call
  496.         }
  497.         break;
  498.  
  499.         case kTrackCache:                    // This is a floppy specific control call
  500.         {            
  501.             err = controlErr;                // The driver does not keep an internal write cache,
  502.                                             // therefore report that we do not support this call
  503.         }
  504.         break;
  505.  
  506.         case kDriveIcon:                    // Return icon displayed during media initialization
  507.         case kMediaIcon:                    // Return icon displayed on desktop for media
  508.         {
  509.             VolumeRecPtr    vol         = nil;        // pointer to our volume record structure
  510.             
  511.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  512.             if ( !vol )                                // If we did not find a drive and volume
  513.             {
  514.                 err = nsDrvErr;                        // report drive not found error
  515.                 break;
  516.             }
  517.                 
  518.             BlockMove(vol->mediaIconPtr, &gDriveIcon.LocationIcon, sizeof(DiskIcon));
  519.  
  520.             // Copy over the DriveInfo string from the global param block        
  521.             PStrCopy(gDriveIcon.LocationString, gTheUTGlobals.DriveInfoString);
  522.  
  523.             // Finally, return the pointer to the icon in csParam
  524.             *(DriverLocationIcon**)pbPtr->csParam = &gDriveIcon;    
  525.  
  526.             err = noErr;                // clear any error from above
  527.         }
  528.         break;
  529.  
  530.         case kDriveInfo:                    // DRIVE INFO request (was from ATA maanger)
  531.         {
  532.             pbPtr->csParam[0] = 0;            // Upper Word is always 0;
  533.             pbPtr->csParam[1] = 0;            // Clear Lower Word
  534.             
  535.             if( gTheUTGlobals.isFloppy == true )
  536.             {
  537.                 // If we currently have a floppy loaded, return the following info
  538.                 pbPtr->csParam[1] =     ( 1 << 11 )     // Drive cardinality (0 - primary, 1 - secondary)
  539.                                     |    ( 0 << 10 )        // Media removability ( 0 - removable, 1 - fixed )
  540.                                     |     ( 0 << 9 )         // Interface ( 0 - floppy, 1 - SCSI )        
  541.                                     |     ( 1 << 8 )         // Location ( 0 - internal, 1 - external )
  542.                                                         // Bits 4,5,6,7 are reserved
  543.                                     |     4;                // Drive Type ( use 4 for SuperDrive for compatibility )
  544.             }
  545.             else if(gTheUTGlobals.theDrive.blockSize != 0)
  546.             {
  547.                 // If we currently have a cartridge loaded, return the following info
  548.                 pbPtr->csParam[1] =     ( 1 << 11 )     // Drive cardinality (0 - primary, 1 - secondary)
  549.                                     |    ( 0 << 10 )        // Media removability ( 0 - removable, 1 - fixed )
  550.                                     |     ( 1 << 9 )         // Interface ( 0 - floppy, 1 - SCSI )        
  551.                                     |     ( 1 << 8 )         // Location ( 0 - internal, 1 - external )
  552.                                                         // Bits 4,5,6,7 are reserved
  553.                                     |     1;                // Drive Type ( use 1 for Unknown drive (Not a floppy) )
  554.             }
  555.  
  556.         }
  557.         break;
  558.  
  559.         
  560.         case kDriverConfigureCode:
  561.         {
  562.             DriverConfigParam             *configPtr;            // local pointer to drive config structure
  563.                         
  564.             configPtr = (DriverConfigParam *) pbPtr;
  565.             switch(configPtr->driverConfigureSelector) 
  566.             {
  567.                 case kdgFlush:
  568.                 {
  569.                     // Since cache flushing differs between devices, this is not implemented in the sample driver.
  570.                     err = controlErr;
  571.                 }
  572.                 break;
  573.                 
  574.                 default:
  575.                 {
  576.                     err = controlErr;
  577.                 }
  578.                 break;
  579.             }
  580.         }
  581.         break;
  582.  
  583.         case kcsSetBootPartitionCode:                // Set Startup Partition
  584.         {
  585.             // Since there is currently no way to boot of USB,
  586.             // this control call will report that it is not supported
  587.             err = controlErr;
  588.         }
  589.         break;
  590.  
  591.         case kRegisterPartition:            // Register New Partition
  592.         {
  593.             // PC Exchange will call this function when it wants to redefine a partition.  It will
  594.             // pass in the drive queue element pointer of the partition to redefine, the
  595.             // new starting physical block offset, and the new block length.
  596.  
  597.             DrvQElPtr        theDrvQEl;                                // drive queue element pointer
  598.             VolumeRecPtr    vol         = nil;                        // pointer to our volume record structure
  599.             UInt32            *altParams;                                // local pointer to alternate control parameters
  600.     
  601.             altParams = (UInt32 *)&pbPtr->csParam[0];                // alternate parameters
  602.             
  603.             err = nsDrvErr;                                            // assume an invalid volume
  604.             theDrvQEl = (DrvQElPtr) altParams[THE_DRIVE];
  605.             if ( theDrvQEl )                                        // if valid queue element pointer
  606.             {
  607.                 vol = GetVolume(drive, theDrvQEl->dQDrive, 0);
  608.                 if ( vol )                                            // and valid volume reference
  609.                 {
  610.                     vol->partoffset = altParams[THE_PHYS_START];    // new partition offset
  611.                     vol->curoffset = vol->partoffset;                // current offset changes also
  612.                     vol->partblks = altParams[THE_PHYS_SIZE];        // new partition size
  613.                     
  614.                     UpdateQ(theDrvQEl->dQDrive, vol->partblks);        // Update drive queue capacity
  615.                     vol->partmounted = true;                        // volume will be mounted by PCX
  616.                     err = noErr;                                    // clear error
  617.                 }
  618.             }
  619.         }
  620.         break;
  621.             
  622.         case kGetADrive:                    // Get A Drive (Create New Partition)
  623.         {            
  624.             // PC Exchange calls this function to add a new partition.  The new partition's DrvQElPtr is
  625.             // returned. NOTE: If the driver handles multiple drives note that PC Exchange does not pass
  626.             // in the physical drive number on which to create the new partition.  However, the
  627.             // DrvQElPtr stored at the pointer passed in is for another partition on the drive.
  628.             UInt32            *altParams;                        // local pointer to alternate control parameters
  629.     
  630.             altParams = (UInt32 *)&pbPtr->csParam[0];        // alternate parameters
  631.         
  632.             if (!altParams[THE_VAR_QUEL])        // verify a valid queue element handle
  633.                 err = paramErr;
  634.             else
  635.             {
  636.                 // create a new volume record and DrvQEl associated with the physical drive.
  637.                 // The new volume starts at offset 0 and has no capacity yet. By default, the
  638.                 // partition will not have a partition map entry on the media.
  639.                 VolumeRecPtr    vol         = nil;            // pointer to our volume record structure
  640.                     
  641.                 vol = CreateVolume(drive, NextPartitionID(drive), 0, 0 );
  642.                 if ( vol ) 
  643.                 {
  644.                     UInt16 volNumber;
  645.                     
  646.                     vol->vRefNum = NextQDrive();        // assign a logical drive number
  647.                     volNumber = vol->vRefNum;
  648.                     
  649.                     NativeAddDrive(gTheUTGlobals.drvrRefNum, volNumber,(DrvQElPtr)  &vol->driveStatus.qLink);
  650.  
  651.                     // Return the DrvQElPtr at the location passed in…
  652.                     *((DrvQElPtr*)altParams[THE_VAR_QUEL]) = (DrvQElPtr) &vol->driveStatus.qLink;
  653.                 }
  654.                 else
  655.                     err = controlErr;
  656.             }
  657.         }
  658.         break;
  659.             
  660.         case kMediaPowerCSCode:                        // Set Power Mode
  661.         {
  662.             // The driver does not support power control modes
  663.             // report back a controlErr.
  664.             err = controlErr;
  665.         }
  666.         break;
  667.  
  668.         case 500:
  669.         {
  670.             AbsoluteTime    theWait;
  671.         
  672.             IfDebugging("\pSet Dispatch Table");
  673.             gItsTheDispatchTable = ( StorageClassDispatchTablePtr ) *((UInt32 *)(&pbPtr->csParam[0]));                // The class's dispatch table
  674.  
  675.             (void) ((gItsTheDispatchTable)->pStorageControl)(kControlDisableRemoval, nil);
  676.             theWait = DurationToAbsolute(durationSecond);
  677.             theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  678.             err = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  679.         }
  680.         break;
  681.  
  682.         case 21315:        // Disk copy format and copy
  683.         {
  684.             UInt16        theFormat;
  685.             UInt8        *theDiskImage;
  686.             
  687.             //SysDebugStr("\pDiskCopy");
  688.  
  689.             if( gTheUTGlobals.isWriteProtected == true)
  690.             {
  691.                 err = wPrErr;            // disk is write protected
  692.                 break;
  693.             }
  694.             
  695.             theFormat = pbPtr->csParam[0];
  696.             theDiskImage = (UInt8 *) *((UInt32 *) &pbPtr->csParam[1]);        // Extract the pointer to the data
  697.             gTheUTGlobals.doInternalReadWrite = true;
  698.             err = ReadWriteBlock( &gTheUTGlobals, 0, gTheUTGlobals.theDrive.capacity,(Ptr)  theDiskImage, true, false );
  699.             gTheUTGlobals.doInternalReadWrite = false;
  700.  
  701.             // an Error has occured                    
  702.             if ( err != noErr )
  703.             {
  704.                 // report an error
  705.                 err = paramErr;
  706.             }
  707.         }
  708.         break;
  709.         
  710.         default:
  711.         {
  712.             err = controlErr;
  713.         }
  714.         break;
  715.     }
  716.         
  717.     //SysDebugStr("\pEnd Control Call");
  718.     return(err);
  719. }
  720.  
  721. //    May run at interrupt level, CANNOT allocate or move memory.
  722.  
  723. OSStatus DriverStatusCmd (    AddressSpaceID    addressSpaceID,
  724.                             IOCommandID        ioCommandID,
  725.                             IOCommandKind    ioCommandKind,
  726.                             ParmBlkPtr        pb)
  727. {
  728. #pragma unused (addressSpaceID, ioCommandID, ioCommandKind)
  729.  
  730.     OSStatus            err            = noErr;
  731.     CntrlParamPtr         pbPtr;
  732.     DriveRecPtr            drive         = &gTheUTGlobals.theDrive;
  733.  
  734. #if 0
  735.     // This can be removed because this routine never invokes device access
  736.     // and always complete immediately.  If this a status call is added that
  737.     // doesn't complete immediately, then this routine will have to guarantee
  738.     // that the parameter block that it uses is not already in use.
  739.     BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  740.     
  741.     gTheUTGlobals.drivePB.theIOPB            = nil;
  742.     gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  743.     gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  744.     gTheUTGlobals.drivePB.retryCount         = 0;
  745.     gTheUTGlobals.drivePB.doWrite             = false;
  746. #endif
  747.  
  748.     pbPtr = (CntrlParamPtr) pb;
  749.  
  750.     // Finish processing the status call…
  751.     switch(pbPtr->csCode) 
  752.     {
  753.         case kReturnFormatList:
  754.         {
  755.             if( gTheUTGlobals.isFloppy == false )
  756.             {
  757.                 err = statusErr;
  758.             }
  759.             else
  760.             {
  761.                 typedef struct FormatList
  762.                 {
  763.                     UInt32        NumberBlock;
  764.                     UInt8        TSSValid         : 1;
  765.                     UInt8        IsCurrentFormat : 1;
  766.                     UInt8        CanFormat        : 1;
  767.                     UInt8        Density            : 1;
  768.                     UInt8        NoSides            : 4;
  769.                     UInt8        SecPerTrack;
  770.                     UInt16        NumberTracks;
  771.                 } FormatList;
  772.                 
  773.                 UInt8            returnNumber;
  774.                 UInt8            totalFormats = 2;
  775.                 FormatList        theFormats[2];
  776.                 
  777.                 // Setup the Formats
  778.                 // Double Density Floppy Disk
  779.                 theFormats[0].NumberBlock        = 1440;
  780.                 theFormats[0].TSSValid            = 1;
  781.                 theFormats[0].Density            = 0;            // 0 means single density, 1 means Double Density
  782.                 theFormats[0].NoSides            = 2;
  783.                 theFormats[0].SecPerTrack        = 9;
  784.                 theFormats[0].NumberTracks        = 80;
  785.                 
  786.                 // High Density Floppy Disk
  787.                 theFormats[1].NumberBlock        = 2880;
  788.                 theFormats[1].TSSValid            = 1;
  789.                 theFormats[1].Density            = 1;
  790.                 theFormats[1].NoSides            = 2;
  791.                 theFormats[1].SecPerTrack        = 18;
  792.                 theFormats[1].NumberTracks        = 80;
  793.  
  794.                 if( drive->capacity < 0x600 )
  795.                 {
  796.                     theFormats[0].CanFormat            = 0;        // 0 means we can format this disk as 720K
  797.                     theFormats[1].CanFormat            = 1;        // 1 means we can not format this disk as 1.44M
  798.                     theFormats[0].IsCurrentFormat    = 1;         // 1 means 720K MFM Disk is installed
  799.                     theFormats[1].IsCurrentFormat     = 0;        // 0 means 1.44M MFM Disk is not installed
  800.                 }
  801.                 else
  802.                 {
  803.                     theFormats[0].CanFormat            = 1;        // 1 means we can not format this disk as 720K
  804.                     theFormats[1].CanFormat            = 0;        // 0 means we can format this disk as 1.44M
  805.                     theFormats[0].IsCurrentFormat    = 0;         // 0 means 720K MFM Disk is not installed
  806.                     theFormats[1].IsCurrentFormat     = 1;        // 1 means 1.44M MFM Disk is installed
  807.                 }
  808.  
  809.                 returnNumber = *((UInt16 *) &pbPtr->csParam[0]);
  810.                 if( totalFormats < returnNumber )
  811.                 {
  812.                     returnNumber = totalFormats;
  813.                 }
  814.                 
  815.                 BlockCopy( (Ptr) &theFormats[0], (Ptr) (*((UInt32 *) &pbPtr->csParam[1])), sizeof(FormatList) * returnNumber);
  816.                 *((UInt16 *) &pbPtr->csParam[0]) = returnNumber;
  817.             }
  818.         }
  819.         break;
  820.     
  821.         case kDriveStatus:
  822.         {
  823.             VolumeRecPtr        vol         = nil;
  824.             
  825.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  826.             BlockMove(&vol->driveStatus, (UInt8 *) &pbPtr->csParam[0], sizeof(DrvSts));
  827.         }
  828.         break;
  829.     
  830.         case kMFMStatus:
  831.         {
  832.             if( gTheUTGlobals.isFloppy == false )
  833.             {
  834.                 err = statusErr;
  835.             }
  836.             else
  837.             {
  838.                 pbPtr->csParam[0] = -3;                            // PC Industry standard MFM (no GCR support)
  839.                 pbPtr->csParam[1] = -1;                            // MFM Disk installed
  840.                 
  841.                 if( drive->capacity < 0x600 )
  842.                 {
  843.                     pbPtr->csParam[2] = 0;                        // 720K MFM Disk installed
  844.                 }
  845.                 else
  846.                 {
  847.                     pbPtr->csParam[2] = -1;                        // 1.44M MFM Disk installed
  848.                 }
  849.                 
  850.                 pbPtr->csParam[3] = -5;                            // Generic PC Floppy Disk Controller
  851.             }
  852.         }
  853.         break;
  854.     
  855.         case kDriverGestaltCode:
  856.         {
  857.             DriverGestaltParam             *gestaltPtr;            // local pointer to drive gestalt structure
  858.             VolumeRecPtr                vol = nil;
  859.  
  860.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  861.             if (vol)
  862.                 drive = (DriveRecPtr) vol->drivePtr;            // get its drive record
  863.  
  864.             gestaltPtr = (DriverGestaltParam *) pbPtr;
  865.             switch(gestaltPtr->driverGestaltSelector) 
  866.             {
  867.                 case kdgVersion:
  868.                 {
  869.                     // Return information on the driver version
  870.                     NumVersion* numVersion;
  871.                     
  872.                     numVersion = GetDriverGestaltVersionResponse(gestaltPtr);
  873.                     
  874.                     numVersion->majorRev =            kStorageHexMajorVers;
  875.                     numVersion->minorAndBugRev =    kStorageHexMinorVers;
  876.                     numVersion->stage =                kStorageReleaseStage;
  877.                     numVersion->nonRelRev =            kStorageCurrentRelease;
  878.                 }
  879.                 break;
  880.                 
  881.                 case kdgDeviceType:
  882.                 {
  883.                     // Return the type of device--either floppy disk or removable disk
  884.                     DriverGestaltDevTResponse* deviceTypeResponse = GetDriverGestaltDevTResponse(gestaltPtr);
  885.  
  886.                     if( gTheUTGlobals.isFloppy == true )
  887.                     {
  888.                         deviceTypeResponse->deviceType = kdgFloppyType;
  889.                     }
  890.                     else
  891.                     {
  892.                         deviceTypeResponse->deviceType = kdgRemovableType;
  893.                     }
  894.                 }
  895.                 break;
  896.                 
  897.                 case kdgInterface:
  898.                 {
  899.                     // Return the interface of the drive in ioVRefNum.
  900.                     DriverGestaltIntfResponse* interfaceResponse = GetDriverGestaltIntfResponse(gestaltPtr);        
  901.  
  902.                     interfaceResponse->interfaceType = 'USB ';
  903.                 }
  904.                 break;
  905.  
  906.                 case kdgSync:
  907.                 {
  908.                     // Return true if the driver supports only synchronous behavior
  909.                     DriverGestaltSyncResponse*    syncResponse = GetDriverGestaltSyncResponse(gestaltPtr);
  910.  
  911.                     syncResponse->behavesSynchronously = false;
  912.                 }
  913.                 break;
  914.                     
  915.                 case kdgBoot:
  916.                 {
  917.                     // Return the ID of the boot device for PRAM storage
  918.                     if (!drive || !vol)
  919.                         err = nsDrvErr;
  920.                     else
  921.                     {
  922.                         // Since booting from USB is not yet supported, return statusErr.
  923.                         err = statusErr;
  924.                     }
  925.                 }
  926.                 break;
  927.  
  928.                 case kdgWide:
  929.                 {
  930.                     // Return whether driver supports large volume addressing (> 4GByte)
  931.                     // Unless our cartridges are greater than 4 GB, we don't support
  932.                     // or need to support wide block addressing
  933.                     Boolean* response = GetDriverGestaltBooleanResponse(gestaltPtr);
  934.                     
  935.                     *response = false; 
  936.                 }
  937.                 break;
  938.  
  939.                 case kdgPurge:        // Return if we can be closed and purged from memory.
  940.                 {
  941.                     DriverGestaltPurgeResponse* response = GetDriverGestaltPurgeResponse(gestaltPtr);
  942.                                         
  943.                     // Currently the driver cannot be closed or purged once installed
  944.                     response->purgePermission = kmNoCloseNoPurge;
  945.  
  946.                     response->purgeDriverPointer = (Ptr) nil;
  947.                 }
  948.                 break;
  949.                     
  950.                 case kdgSupportsSwitching:
  951.                 {
  952.                     // Return whether driver supports low power control call (csCode = 70h)
  953.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false;
  954.                 }
  955.                 break;
  956.                     
  957.                 case kdgSupportsPowerCtl:
  958.                 {
  959.                     // Return whether driver supports low power control call (csCode = 70h)
  960.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false;
  961.                 }
  962.                 break;
  963.                     
  964.                 case kdgAPI:
  965.                 {
  966.                     // Return whether driver supports PC-Exchange Control and Status calls
  967.                     // related to partitioning.
  968.                     DriverGestaltAPIResponse* apiResponse = GetDriverGestaltAPIResponse(gestaltPtr);
  969.                     
  970.                     apiResponse->partitionCmds = true;
  971.                 }
  972.                 break;
  973.                 
  974.                 case kdgFlush:
  975.                 {
  976.                     // Return whether driver supports the Cache flush Control call,
  977.                     // and whether the finder should tell us to flush our cache
  978.                     // Since cache flushing differs between devices, this is not implemented in the sample driver.
  979.                     DriverGestaltFlushResponse* response = GetDriverGestaltFlushResponse(gestaltPtr);
  980.  
  981.                     response->canFlush = false;
  982.                     response->needsFlush = false;
  983.                 }
  984.                 break;
  985.  
  986.                 case kdgEject:
  987.                 {
  988.                     // Return whether driver wants eject call for shutdown and restart
  989.                     // Eject on restart or shutdown
  990.                     DriverGestaltEjectResponse* response = GetDriverGestaltEjectResponse(gestaltPtr);
  991.                     response->ejectFeatures = 0L; // Clear both Dont Eject Bits, kShutDownDontEject and kRestartDontEject.
  992.                 }
  993.                 break;
  994.  
  995.                 case kdgVMOptions:
  996.                 {
  997.                     // Return whether drive can be used for Virtual Memory
  998.                     // Don't support use of media for VM
  999.                     DriverGestaltVMOptionsResponse* response = GetDriverGestaltVMOptionsResponse(gestaltPtr);
  1000.                     response->vmOptions = kAllowVMNoneMask;
  1001.                 }
  1002.                 break;
  1003.  
  1004.                 case kdgMediaInfo:
  1005.                 {
  1006.                     // Return back specific information about our media
  1007.                     DriverGestaltMediaInfoResponse* response = GetDriverGestaltMediaInfoResponse(gestaltPtr);
  1008.                     //response->numberBlocks =    vol->partblks;
  1009.                     response->numberBlocks =    gTheUTGlobals.theDrive.capacity;
  1010.                     response->blockSize =        gTheUTGlobals.theDrive.blockSize;
  1011.                     if(gTheUTGlobals.theDrive.blockSize == 0)
  1012.                     {
  1013.                         response->mediaType =        kMediaTypeNoMedia;
  1014.                     }
  1015.                     else
  1016.                     {
  1017.                         response->mediaType =        kMediaTypeUnknown;
  1018.                     }
  1019.                 }
  1020.                 break;
  1021.  
  1022.                 /* Return a pointer to a IconFamily ('icns') data structure for */
  1023.                 /* Disk Driver physical drive (formerly in csCode 22) in driverGestaltResponse. */
  1024.                 case kdgPhysDriveIconSuite:
  1025.  
  1026.                 /* Return a pointer to a IconFamily ('icns') data structure for */
  1027.                 /* Disk Driver media (formerly in csCode 21) in driverGestaltResponse. */
  1028.                 case kdgMediaIconSuite:
  1029.                 {
  1030.                     // If the media is currently a floppy, return a statusErr so the system will
  1031.                     // handle the 3D Color icon information
  1032.                     if( gTheUTGlobals.isFloppy == true )
  1033.                     {
  1034.                         if(FloppyMediaIconFamily == nil )
  1035.                         {
  1036.                             err = statusErr;
  1037.                         }
  1038.                         else
  1039.                         {
  1040.                             gestaltPtr->driverGestaltResponse = (UInt32) *FloppyMediaIconFamily;
  1041.                         }
  1042.                     }
  1043.                     else
  1044.                     {
  1045.                         if(CartridgeMediaIconFamily == nil )
  1046.                         {
  1047.                             err = statusErr;
  1048.                         }
  1049.                         else
  1050.                         {
  1051.                             gestaltPtr->driverGestaltResponse = (UInt32) CartridgeMediaIconFamily;
  1052.                         }
  1053.                     }
  1054.                 }
  1055.                 break;
  1056.  
  1057.                 case kdgMediaName:
  1058.                 {
  1059.                     /* Return a pointer to a pascal string describing the Disk Driver (formerly in csCode 21) in driverGestaltResponse. */
  1060.                     gestaltPtr->driverGestaltResponse = (UInt32) &gTheUTGlobals.DriveInfoString;
  1061.                 }
  1062.                 break;
  1063.  
  1064.                 default: 
  1065.                     err = statusErr;        // unknown DriverGestalt selector
  1066.             }
  1067.         }
  1068.         break;
  1069.         
  1070.         case kcsGetBootPartitionStatus:     // Is this the boot partition?
  1071.         {
  1072.             // Since USB drives don't support booting, always report false
  1073.             err = statusErr;
  1074.         }
  1075.         break;
  1076.         
  1077.         case kGetPartInfo:
  1078.         {
  1079.             // PC Exchange will call this function to get info on the specified partition. 
  1080.             // Return the physical drive reference, the starting block offset, and 
  1081.             // the partition ID (any relative non-zero reference).
  1082.     
  1083.             UInt32            *altParams;                // alternate csParams as long words
  1084.             partInfoRecPtr     thePartInfo;
  1085.             VolumeRecPtr    vol = nil;
  1086.             
  1087.             altParams = (UInt32 *) &pbPtr->csParam[0];            // alternate parameters
  1088.             thePartInfo = (partInfoRecPtr)altParams[kPartInfoResponse];
  1089.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  1090.     
  1091.             // SCSIID is not defined for non-SCSI devices.  For now we use the LUN only
  1092.             *((UInt32 *)&thePartInfo->SCSIID) = 0;
  1093.     
  1094.             thePartInfo->physPartitionLoc = vol->partoffset;
  1095.             thePartInfo->partitionNumber = vol->partitionNo;
  1096.         }
  1097.         break;
  1098.     
  1099.         case kMediaPowerCSCode:
  1100.         {
  1101.             // Driver does not support power control modes,
  1102.             // so just return a statusErr
  1103.             err = statusErr;
  1104.         }
  1105.         break;        
  1106.         
  1107.         case 17494:                            // DiskCopy version supported
  1108.         {
  1109.             pbPtr->csParam[0] = 0x0410;        // We support the Diskcopy 4.1 API
  1110.         }
  1111.         break;
  1112.         
  1113.         default:                            // Unrecognized status call
  1114.         {
  1115.             err = statusErr;
  1116.         }
  1117.         break;
  1118.     }
  1119.     //SysDebugStr("\pEnd Status Call");
  1120.     
  1121.     return(err);
  1122. }
  1123.  
  1124. //    May run at interrupt level, CANNOT allocate or move memory.
  1125.  
  1126. OSStatus DriverReadCmd (    AddressSpaceID    addressSpaceID,
  1127.                             IOCommandID        ioCommandID,
  1128.                             IOCommandKind    ioCommandKind,
  1129.                             ParmBlkPtr        pb)
  1130. {
  1131. #pragma unused ( addressSpaceID )
  1132.     OSStatus        err = ioErr;
  1133.  
  1134.     if( gTheUTGlobals.diskInDrive == false)
  1135.     {
  1136.         err = ioErr;
  1137.     }
  1138.     else
  1139.     {
  1140.         //SysDebugStr("\pRead Call;g");
  1141.         // Clear out the Drive request PB
  1142.         BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  1143.         
  1144.         gTheUTGlobals.drivePB.theIOPB            = pb;
  1145.         gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  1146.         gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  1147.         gTheUTGlobals.drivePB.retryCount         = 0;
  1148.         gTheUTGlobals.drivePB.doWrite             = false;
  1149.     
  1150.         err = DoReadWriteCommand( &gTheUTGlobals, false);
  1151.     }
  1152.     return(err);
  1153. }
  1154.  
  1155. //    May run at interrupt level, CANNOT allocate or move memory.
  1156.  
  1157. OSStatus DriverWriteCmd (    AddressSpaceID    addressSpaceID,
  1158.                             IOCommandID        ioCommandID,
  1159.                             IOCommandKind    ioCommandKind,
  1160.                             ParmBlkPtr        pb)
  1161. {
  1162. #pragma unused ( addressSpaceID )
  1163.     OSStatus        err = ioErr;
  1164.  
  1165.     if( gTheUTGlobals.diskInDrive == false )
  1166.     {
  1167.         err = ioErr;
  1168.     }
  1169.     else
  1170.     {
  1171.         //SysDebugStr("\pWrite Call");
  1172.         // Clear out the Drive request PB
  1173.         BlockZero((Ptr) &gTheUTGlobals.drivePB, sizeof(DriveRequestPB));
  1174.     
  1175.         gTheUTGlobals.drivePB.theIOPB            = pb;
  1176.         gTheUTGlobals.drivePB.ioCommandID         = ioCommandID;
  1177.         gTheUTGlobals.drivePB.ioCommandKind     = ioCommandKind;
  1178.         gTheUTGlobals.drivePB.retryCount         = 0;
  1179.         gTheUTGlobals.drivePB.doWrite             = true;
  1180.             
  1181.         err = DoReadWriteCommand( &gTheUTGlobals, true);
  1182.     }
  1183.     
  1184.     return(err);
  1185. }
  1186.  
  1187. //    May run at interrupt level, CANNOT allocate or move memory.
  1188.  
  1189. OSStatus DriverKillIOCmd (ParmBlkPtr pb)
  1190. {
  1191. #pragma unused (pb)
  1192.     OSStatus    err = noErr;
  1193.  
  1194.     return (err);
  1195. }
  1196.  
  1197. void InstallInSleepQueue(OurSleepQRecPtr ourSleepQRec)    
  1198. {
  1199.     Boolean hasPMDispatch;
  1200.     SInt32    status;
  1201.  
  1202.     // To do most power management we must have the Power Manager Dispatch routines.      
  1203.     if (Gestalt(gestaltPowerMgrAttr, &status) == 0)                                
  1204.         hasPMDispatch = (status & (1 << gestaltPMgrDispatchExists)) ? true : false;    
  1205.  
  1206.     if (hasPMDispatch)                                                            
  1207.     {
  1208.         // Power Manager manages a spindown timer for the internal drive.  When the
  1209.         // timer expires it calls all routines registered in the HD Queue, and then,
  1210.         // if PG&E is present (Powerbooks) it will turn off power to the drive.
  1211.         // If PG&E is not present (Desktops) Power Manager can't turn off power and
  1212.         // expects one of the queue routines to reduce drive power instead. 
  1213.         //  Therefore, if we manage the internal drive we should be in the HD Queue.
  1214.  
  1215.             ourSleepQRec->theSleepQRec.sleepQLink = nil;                                
  1216.             ourSleepQRec->theSleepQRec.sleepQType = sleepQType;                            
  1217.             ourSleepQRec->theSleepQRec.sleepQProc = NewSleepQProc( &SleepNotification);        
  1218.             //ourSleepQRec->theSleepQRec.sleepQProc = (SleepQUPP) NewSleepQProc( &SleepNotification);        
  1219.             ourSleepQRec->theSleepQRec.sleepQFlags = 0;                // reserved    
  1220.             ourSleepQRec->isInSleep = false;    
  1221.             SleepQInstall( (SleepQRecPtr) ourSleepQRec );                            
  1222.     }                                                                            
  1223. }                                                                                
  1224.  
  1225. void RemoveFromSleepQueue(OurSleepQRecPtr ourSleepQRec)    
  1226. {
  1227.     Boolean hasPMDispatch;
  1228.     SInt32    status;
  1229.  
  1230.     // To do most power management we must have the Power Manager Dispatch routines.      
  1231.     if (Gestalt(gestaltPowerMgrAttr, &status) == 0)                                
  1232.         hasPMDispatch = (status & (1 << gestaltPMgrDispatchExists)) ? true : false;    
  1233.  
  1234.     if (hasPMDispatch)                                                            
  1235.     {
  1236.         SleepQRemove( (SleepQRecPtr) ourSleepQRec );
  1237.     }
  1238. }                                                                                
  1239.  
  1240.  
  1241. long SleepNotification(long message, SleepQRecPtr sleepRec)
  1242. {
  1243.     OurSleepQRecPtr    ourSleepRec = (OurSleepQRecPtr) sleepRec;
  1244.     SInt32             response = noErr;                // assume we accept command
  1245.  
  1246.     switch(message)
  1247.     {
  1248.         case dozeRequest:                    // ##### Request to doze ######
  1249.         case dozeDemand:                    // ##### Going to doze now ######
  1250.         case sleepRequest:                    // ##### Request to sleep ######
  1251.         case sleepDemand:                    // ##### Going to sleep now ######
  1252.         case sleepNow:
  1253.             ourSleepRec->isInSleep = true;
  1254.             break;
  1255.  
  1256.         case sleepWakeUp:                    // ##### Wakeup from sleep ######
  1257.         case sleepRevoke:                    // ##### Someone denied sleep ######
  1258.         case dozeWakeUp:                    // ##### Wakeup from doze ######
  1259.             ourSleepRec->isInSleep = false;
  1260.             break;
  1261.     }
  1262.  
  1263.     return(response);
  1264. }
  1265.  
  1266.  
  1267. OSStatus MountSecondaryInterrupt( void *p1, void *p2)
  1268. {
  1269. #pragma unused ( p1, p2 )
  1270.     OSStatus             status;
  1271.     AbsoluteTime        theWait;
  1272.     UInt32                classDriverStatus;
  1273.  
  1274.     IfDebugging("\pMountSecondaryInterrupt");    
  1275.     gInterruptTimer = 0;
  1276.     
  1277.     if(gItsTheDispatchTable)
  1278.     {
  1279.         // Check if class driver is configured, if not setup another interrupt
  1280.         status = ((gItsTheDispatchTable)->pStorageStatus)(kStatusConfiguration, &classDriverStatus);
  1281.         if (status == noErr)
  1282.         {
  1283.             if ((classDriverStatus == kConfigureInProgress) || (gTheUTGlobals.theSleepQRec.isInSleep == true))
  1284.             {
  1285.                 theWait = DurationToAbsolute(durationSecond);
  1286.                 theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1287.                 status = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1288.                 
  1289.                 return noErr;
  1290.             } 
  1291.             else if (classDriverStatus == kConfigureComplete)
  1292.             {
  1293.                 gTheUTGlobals.currentExecutionState = kMountStartState;
  1294.                 MountTheCartridge( &gTheUTGlobals );
  1295.             }
  1296.         }
  1297.     }
  1298.     
  1299.     return noErr;
  1300. }
  1301.  
  1302.  
  1303. void MountTheCartridge( void *theCurrentPB )
  1304. {
  1305.     OSStatus         err = noErr;
  1306.     AbsoluteTime    oneSecondWait;
  1307.     DriveRec        *drive;
  1308.     UTDriverGlobals    *ourPB;
  1309.  
  1310.     ourPB = ((UTDriverGlobals *) theCurrentPB);
  1311.     drive = &ourPB->theDrive;
  1312.     oneSecondWait = DurationToAbsolute(durationSecond);
  1313.     //oneSecondWait = DurationToAbsolute(durationMillisecond * 20);
  1314.     switch (ourPB->currentExecutionState )
  1315.     {
  1316.         case kMountStartState:
  1317.         {
  1318.             ourPB->currentExecutionState = kMountTURDoneState;
  1319.             err = TUR( ourPB, &MountTheCartridge);
  1320.         }
  1321.         break;
  1322.         
  1323.         case kMountTURDoneState:
  1324.         {
  1325.             ourPB->currentExecutionState = kMountRequestSenseDoneState;
  1326.             err = RequestSense( ourPB, &MountTheCartridge);
  1327.         }
  1328.         break;
  1329.         
  1330.         case kMountRequestSenseDoneState:
  1331.         {
  1332.             if((ourPB->sense[12] == 0x00) && (ourPB->drivePB.executePB.status == noErr))
  1333.             {
  1334.                 ourPB->currentExecutionState = kMountGetGeometryDoneState;
  1335.                 ourPB->getCapacity.lastLogicalBlock = 0;
  1336.                 ourPB->getCapacity.blockLength = 0;
  1337.                 err = GetMediaGeometry( ourPB, &MountTheCartridge);
  1338.             }
  1339.             else
  1340.             {
  1341.                 ourPB->currentExecutionState = kMountStartState;
  1342.                 oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1343.                 err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1344.                 err = noErr;
  1345.             }
  1346.         }
  1347.         break;
  1348.  
  1349.         case kMountGetGeometryDoneState:
  1350.         {
  1351.             ReadCapacityData        *getGeometry;
  1352.             
  1353.             getGeometry = (ReadCapacityData *) &ourPB->getCapacity;
  1354.  
  1355.             drive->capacity = getGeometry->lastLogicalBlock+1; // Be sure to add the zero block in
  1356.             drive->blockSize = getGeometry->blockLength;
  1357.             
  1358.             if((drive->capacity == 0) || (drive->blockSize == 0))
  1359.             {
  1360.                 //Find out why the capacity is zero
  1361.                 ourPB->currentExecutionState = kMountCheckGetGeometryErrorDone;
  1362.                 err = RequestSense( ourPB, &MountTheCartridge);
  1363.                 break;
  1364.             }
  1365.  
  1366.             if(    drive->capacity >0x0C00)
  1367.             {
  1368.                 ourPB->isFloppy = false;
  1369.             }
  1370.             else
  1371.             {
  1372.                 ourPB->isFloppy = true;
  1373.             }
  1374.             
  1375.             ourPB->diskInDrive = true;
  1376.             
  1377.             // Check to see if media is write protected
  1378.             ourPB->currentExecutionState = kMountCheckWriteProtectDoneState;
  1379.             err = CheckWriteProtect(ourPB, &MountTheCartridge);
  1380.         }
  1381.         break;
  1382.  
  1383.         case kMountCheckGetGeometryErrorDone:
  1384.         {
  1385.             if( (ourPB->sense[12] == 0x30) && (ourPB->sense[13] == 0x01))
  1386.             {
  1387.                 // We have an unformatted cartridge, find out what format it can have
  1388.                 ourPB->currentExecutionState = kMountReadPossibleCapacitiesDone;
  1389.                 err = ReadFormatCapacity(ourPB, &MountTheCartridge);
  1390.             }
  1391.             else
  1392.             {
  1393.                 ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1394.                 oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1395.                 err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1396.             }
  1397.         }
  1398.         break;
  1399.         
  1400.         case kMountReadPossibleCapacitiesDone:
  1401.         {
  1402.             if( (ourPB->sense[3] >= 0x08) && (ourPB->sense[8] & 0x01 == 0x01))
  1403.             {
  1404.                 // this is the maximum format for this cartridge
  1405.                 drive->capacity = *((UInt32 *) &ourPB->sense[4]);
  1406.                 drive->blockSize = *((UInt16 *) &ourPB->sense[10]);
  1407.  
  1408.                 if(    drive->capacity >0x0C00)
  1409.                 {
  1410.                     ourPB->isFloppy = false;
  1411.                 }
  1412.                 else
  1413.                 {
  1414.                     ourPB->isFloppy = true;
  1415.                 }
  1416.                 
  1417.                 // Check to see if media is write protected
  1418.                 ourPB->currentExecutionState = kMountCheckWriteProtectDoneState;
  1419.                 err = CheckWriteProtect(ourPB, &MountTheCartridge);
  1420.                 break;
  1421.             }
  1422.  
  1423.             // An error occurred.  It could be:
  1424.             //        1. we didn't get a complete descriptor
  1425.             //         2. the drive doesn't recognize this media type 
  1426.             // Reset and try again. ( Should we possibly eject the media instead??)
  1427.             ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1428.             oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1429.             err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1430.         }
  1431.         break;
  1432.         
  1433.         case kMountCheckWriteProtectDoneState:
  1434.         {
  1435.             if ( ( ourPB->sense[3] & 0x80 ) != 0 )
  1436.             {
  1437.                 ourPB->isWriteProtected = true;
  1438.             }
  1439.             else
  1440.             {
  1441.                 ourPB->isWriteProtected = false;
  1442.             }
  1443.             
  1444.             ourPB->currentExecutionState = kMountPreventRemovalDoneState;
  1445.             err = PreventAllowRemoval(ourPB, true, &MountTheCartridge);
  1446.         }
  1447.         break;
  1448.         
  1449.         case kMountPreventRemovalDoneState:
  1450.         {
  1451.             // The device is now mounted, and the Media is locked in place.
  1452.             // There is nothing left for us to do, so just break.
  1453.             if (InstallDrive( drive, true ) == false)
  1454.             {
  1455.                 gTheUTGlobals.currentExecutionState = kEjectStartState;
  1456.                  EjectTheCartridge( &gTheUTGlobals );
  1457.             }
  1458.         }
  1459.         break;
  1460.     }
  1461. }
  1462.  
  1463. void EjectTheCartridge( void *theCurrentPB )
  1464. {
  1465.     OSStatus         err = noErr;
  1466.     UTDriverGlobals    *ourPB;
  1467.  
  1468.     ourPB = ((UTDriverGlobals *) theCurrentPB);
  1469.     switch (ourPB->currentExecutionState )
  1470.     {
  1471.         case kEjectStartState:
  1472.         {
  1473.             ourPB->currentExecutionState = kEjectAllowRemovalDone;
  1474.             err = PreventAllowRemoval(ourPB, false, &EjectTheCartridge); // Allow for Ejects
  1475.         }
  1476.         break;
  1477.         
  1478.         case kEjectAllowRemovalDone:
  1479.         {
  1480.             ourPB->currentExecutionState = kEjectCartridgeDone;
  1481.             EjectCartridge(&gTheUTGlobals, EjectTheCartridge);
  1482.         }
  1483.         break;
  1484.         
  1485.         case kEjectCartridgeDone:
  1486.         {
  1487.             AbsoluteTime            theWait;
  1488.             StorageExecuteCommandPB    *commandPB;
  1489.             DriveRec                *drive;
  1490.         
  1491.             drive = &ourPB->theDrive;
  1492.             RemoveDrive(drive);
  1493.             commandPB = &ourPB->drivePB.executePB;                          // use a pointer to the executePB field
  1494.             ourPB->drivePB.status = commandPB->status;
  1495.             if(commandPB->status == noErr)
  1496.             {
  1497.                 ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1498.                 theWait = DurationToAbsolute(durationSecond);
  1499.                 theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1500.                 SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1501.             }
  1502.             
  1503.             drive->capacity = 0;
  1504.             drive->blockSize = 0;
  1505.             ourPB->isFloppy = false;
  1506.             ourPB->diskInDrive = false;
  1507.         }
  1508.         break;
  1509.     }
  1510. }
  1511.  
  1512. OSStatus FormatCompletionInterrupt( void *p1, void *p2)
  1513. {
  1514. #pragma unused ( p1, p2 )
  1515.     gInterruptTimer = 0;
  1516.     gTheUTGlobals.currentExecutionState = kFormatWaitDoneState;
  1517.     FormatTheCartridge( &gTheUTGlobals );
  1518.     return noErr;
  1519. }
  1520.  
  1521. void FormatTheCartridge( void *theCurrentPB )
  1522. {
  1523.     OSStatus         err = noErr;
  1524.     AbsoluteTime    oneSecondWait;
  1525.     DriveRec        *drive;
  1526.     UTDriverGlobals    *ourPB;
  1527.  
  1528.     ourPB = ((UTDriverGlobals *) theCurrentPB);
  1529.     drive = &ourPB->theDrive;
  1530.     oneSecondWait = DurationToAbsolute(durationSecond);
  1531.  
  1532.     switch (ourPB->currentExecutionState )
  1533.     {
  1534.         case kFormatStartState:
  1535.         {
  1536.             ourPB->currentExecutionState = kFormatDoneState;
  1537.             err = FormatFloppyCartridge( ourPB, &FormatTheCartridge);
  1538.         }
  1539.         break;
  1540.         
  1541.         case kFormatDoneState:
  1542.         {
  1543.             AbsoluteTime    theWait;
  1544.             
  1545.             theWait = DurationToAbsolute(durationSecond*35);
  1546.             theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1547.             SetInterruptTimer( &theWait, &FormatCompletionInterrupt, nil, &gInterruptTimer);
  1548.             err = 1;
  1549.         }
  1550.         break;
  1551.         
  1552.         case kFormatWaitDoneState:
  1553.         {
  1554.             ourPB->currentExecutionState = kFormatRequestSenseDoneState;
  1555.             ourPB->drivePB.retryCount = 0;
  1556.             err = RequestSense( ourPB, &FormatTheCartridge);
  1557.         }
  1558.         break;
  1559.         
  1560.         case kFormatRequestSenseDoneState:
  1561.         {
  1562.             //SysDebugStr("\pRequest is done");
  1563.             if(ourPB->drivePB.executePB.status == noErr)
  1564.             {
  1565.                 if(ourPB->sense[12] == 0x00)
  1566.                 {
  1567.                     ourPB->currentExecutionState = kFormatGetGeometryDoneState;
  1568.                     ourPB->getCapacity.lastLogicalBlock = 0;
  1569.                     ourPB->getCapacity.blockLength = 0;
  1570.                     err = GetMediaGeometry( ourPB, &FormatTheCartridge);
  1571.                 }
  1572.                 else
  1573.                 {
  1574.                     // The format has failed, inform the OS
  1575.                     err = controlErr;
  1576.                 }
  1577.             }
  1578.             else
  1579.             {
  1580.                 if (ourPB->drivePB.retryCount < kFormatRequestSenseRetryCount)
  1581.                 {
  1582.                     ourPB->currentExecutionState = kFormatRequestSenseDoneState;
  1583.                     ourPB->drivePB.retryCount += 1;
  1584.                     err = RequestSense( ourPB, &FormatTheCartridge);
  1585.                 }
  1586.                 else
  1587.                 {
  1588.                     // The format has failed, inform the OS
  1589.                     ourPB->drivePB.retryCount = 0;
  1590.                     err = controlErr;
  1591.                 }
  1592.             }
  1593.         }
  1594.         break;
  1595.  
  1596.         case kFormatGetGeometryDoneState:
  1597.         {
  1598.             ReadCapacityData        *getGeometry;
  1599.             
  1600.             getGeometry = (ReadCapacityData *) &ourPB->getCapacity;
  1601.  
  1602.             drive->capacity = getGeometry->lastLogicalBlock+1; // Be sure to add the zero block in
  1603.             drive->blockSize = getGeometry->blockLength;
  1604.             
  1605.             if( (ourPB->drivePB.executePB.status != noErr) || (drive->capacity == 0) || (drive->blockSize == 0))
  1606.             {
  1607.                 // The format has failed, inform the OS
  1608.                 err = controlErr;
  1609.                 break;
  1610.             }
  1611.  
  1612.             if(    drive->capacity >0x0C00)
  1613.             {
  1614.                 ourPB->isFloppy = false;
  1615.             }
  1616.             else
  1617.             {
  1618.                 ourPB->isFloppy = true;
  1619.             }
  1620.  
  1621.             ourPB->diskInDrive = true;
  1622.         }
  1623.         break;
  1624.     }
  1625.     
  1626.     ourPB->drivePB.status = err;
  1627.     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  1628. }
  1629.  
  1630.  
  1631. OSStatus CheckWriteProtect(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1632. {
  1633.     OSStatus    status = noErr;
  1634.     
  1635.     IfDebugging("\pCheck Write Protect");
  1636.  
  1637.     if (gItsTheDispatchTable)
  1638.     {
  1639.         StorageExecuteCommandPB    *commandPB;
  1640.         
  1641.         commandPB = &theCurrentPB->drivePB.executePB;                      // use a pointer to the executePB field
  1642.         
  1643.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));            // clear out the PB we will send
  1644.         BlockZero((Ptr) &theCurrentPB->sense[0], 8);
  1645.  
  1646.         commandPB->cdb[0] =            kCmdModeSense;
  1647.         commandPB->cdb[8] =            0x08;
  1648.         commandPB->flags =             kStorageDataIn;                                    // -> Expect a data in transfer
  1649.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];                    // -> Pointer to user buffer
  1650.         commandPB->expectedCount =    8;                                                // -> Expected number of bytes to transfer
  1651.         commandPB->completionProc =    ourCompletion;                                    // -> Completion routine
  1652.         
  1653.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1654.     }
  1655.     
  1656.     return status;
  1657. }
  1658.  
  1659. OSStatus TUR(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1660. {
  1661.     OSStatus    status = noErr;
  1662.     
  1663.     IfDebugging("\pTUR");
  1664.  
  1665.     if (gItsTheDispatchTable)
  1666.     {
  1667.         StorageExecuteCommandPB    *commandPB;
  1668.         
  1669.         commandPB = &theCurrentPB->drivePB.executePB;              // use a pointer to the executePB field
  1670.         
  1671.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));    // clear out the PB we will send
  1672.     
  1673.         commandPB->flags =             kStorageNoData;    
  1674.         commandPB->completionProc =    ourCompletion;
  1675.         
  1676.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1677.     }
  1678.     
  1679.     return status;
  1680. }
  1681.  
  1682. OSStatus RequestSense(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1683. {
  1684.     OSStatus                status = noErr;
  1685.  
  1686.     if (gItsTheDispatchTable)
  1687.     {
  1688.         StorageExecuteCommandPB    *commandPB;
  1689.         
  1690.         commandPB = (StorageExecuteCommandPB *)&theCurrentPB->drivePB.executePB;              // use a pointer to the executePB field
  1691.  
  1692.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1693.         BlockZero((Ptr) &theCurrentPB->sense[0], sizeof(kSenseDataSize));
  1694.         
  1695.         commandPB->cdb[0] =            kCmdRequestSense;
  1696.         commandPB->cdb[4] =            kSenseDataSize;
  1697.         commandPB->flags =             kStorageDataIn;                            // -> Expect a data in transfer
  1698.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];            // -> Pointer to user buffer
  1699.         commandPB->expectedCount =    kSenseDataSize;                            // -> Expected number of bytes to transfer
  1700.         commandPB->completionProc =    ourCompletion;                            // -> Completion routine
  1701.         
  1702.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1703.     }
  1704.     
  1705.     return status;
  1706. }
  1707.  
  1708. OSStatus GetMediaGeometry(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1709. {
  1710.     OSStatus    status = noErr;
  1711.  
  1712.     IfDebugging("\pGetMediaGeometry");
  1713.  
  1714.     if (gItsTheDispatchTable)
  1715.     {
  1716.         ReadCapacityData        *getGeometry;
  1717.         StorageExecuteCommandPB    *commandPB;
  1718.         
  1719.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1720.         getGeometry = &theCurrentPB->getCapacity;
  1721.         
  1722.         getGeometry->lastLogicalBlock = 0;
  1723.         getGeometry->blockLength = 0;
  1724.         
  1725.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1726.  
  1727.         commandPB->cdb[0] =            kCmdReadCapacity;
  1728.         commandPB->flags =             kStorageDataIn;                            // -> Expect a data in transfer
  1729.         commandPB->userBuffer =        (Ptr) getGeometry;                        // -> Pointer to user buffer
  1730.         commandPB->expectedCount =    sizeof(ReadCapacityData);                // -> Expected number of bytes to transfer
  1731.         commandPB->completionProc =    ourCompletion;                            // -> Completion routine
  1732.         
  1733.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1734.     }
  1735.  
  1736.     return status;
  1737. }
  1738.  
  1739.  
  1740. OSStatus ReadFormatCapacity(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1741. {
  1742.     OSStatus    status = noErr;
  1743.  
  1744.     IfDebugging("\pReadFormatCapacity");
  1745.  
  1746.     if (gItsTheDispatchTable)
  1747.     {
  1748.         StorageExecuteCommandPB    *commandPB;
  1749.         
  1750.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1751.  
  1752.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1753.  
  1754.         commandPB->cdb[0] =            kCmdReadFormatCapacities;
  1755.         commandPB->cdb[7] =            0;
  1756.         commandPB->cdb[8] =            0x0C;
  1757.         commandPB->flags =             kStorageDataIn;                                    // -> Expect a data in transfer
  1758.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];                    // -> Pointer to user buffer
  1759.         commandPB->expectedCount =    0x0C;                                            // -> Expected number of bytes to transfer
  1760.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1761.         
  1762.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1763.     }
  1764.  
  1765.     return status;
  1766. }
  1767.  
  1768.  
  1769. OSStatus FormatFloppyCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1770. {
  1771.     OSStatus    status = noErr;
  1772.  
  1773.     IfDebugging("\pFormatFloppyCartridge");
  1774.  
  1775.     if (gItsTheDispatchTable)
  1776.     {
  1777.         StorageExecuteCommandPB    *commandPB;
  1778.  
  1779.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1780.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1781.         BlockZero((Ptr) &theCurrentPB->sense[0], 0x0C);
  1782.  
  1783.         // Set up the data going out
  1784.         // First set up the Defect List Header
  1785.         theCurrentPB->sense[0] = 0;
  1786.         theCurrentPB->sense[1] = 0;
  1787.         theCurrentPB->sense[2] = 0;
  1788.         theCurrentPB->sense[3] = 0x08;
  1789.  
  1790.         *((UInt32 *) &theCurrentPB->sense[4]) = theCurrentPB->theDrive.capacity;
  1791.         *((UInt16 *) &theCurrentPB->sense[10]) = theCurrentPB->theDrive.blockSize;
  1792.         
  1793.         commandPB->cdb[0] =            kCmdFormat;
  1794.         commandPB->cdb[1] =            0x17;
  1795.         commandPB->flags =             kStorageDataOut;                    // -> Expect a data out transfer
  1796.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];        // -> Pointer to user buffer
  1797.         commandPB->expectedCount =    0x0C;                                // -> Expected number of bytes to transfer
  1798.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1799.  
  1800.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1801.     }
  1802.  
  1803.     return status;
  1804. }
  1805.  
  1806.  
  1807. OSStatus EjectCartridge(UTDriverGlobals *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1808. {
  1809.     OSStatus    status = noErr;
  1810.  
  1811.     if (gItsTheDispatchTable)
  1812.     {
  1813.         StorageExecuteCommandPB    *commandPB;
  1814.         
  1815.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1816.  
  1817.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1818.         
  1819.         commandPB->cdb[0] =            kCmdStartStopUnit;
  1820.         commandPB->cdb[4] =            0x02;                            // Unload the Media
  1821.         commandPB->flags =             kStorageNoData;                    // -> Expect a data in transfer
  1822.         commandPB->userBuffer =        nil;                            // -> Pointer to user buffer
  1823.         commandPB->expectedCount =    0;                                // -> Expected number of bytes to transfer
  1824.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1825.         
  1826.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1827.     }
  1828.  
  1829.     return status;
  1830. }
  1831.  
  1832.  
  1833. OSStatus PreventAllowRemoval(UTDriverGlobals *theCurrentPB, Boolean preventRemoval, StorageClassCompletionProcPtr ourCompletion)
  1834. {
  1835.     OSStatus    status = noErr;
  1836.  
  1837.     if (gItsTheDispatchTable)
  1838.     {
  1839.         StorageExecuteCommandPB    *commandPB;
  1840.         
  1841.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1842.  
  1843.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1844.         
  1845.         commandPB->cdb[0] =            kCmdPreventAllowRemoval;
  1846.         if ( preventRemoval == true )
  1847.         {
  1848.             commandPB->cdb[4] =            0x01;                        // Prevent Media Removal
  1849.         }
  1850.         else
  1851.         {
  1852.             commandPB->cdb[4] =            0x00;                        // Allow Media Removal
  1853.         }
  1854.         
  1855.         commandPB->flags =             kStorageNoData;                    // -> Expect a data in transfer
  1856.         commandPB->userBuffer =        nil;                            // -> Pointer to user buffer
  1857.         commandPB->expectedCount =    0;                                // -> Expected number of bytes to transfer
  1858.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1859.         
  1860.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1861.     }
  1862.  
  1863.     return status;
  1864. }
  1865.  
  1866.  
  1867. /* ---------------------Supporting Functions below this point -------------------------- */
  1868. //------------------------------------------------------------------------------
  1869. //    Function:        DRVRPrime
  1870. //    Description:    This is the ATA driver PRIME call that performs
  1871. //                    reading and writing to the device.
  1872. //                    
  1873. //    Input:            ioPB = Pointer to caller's I/O parameter block
  1874. //                    dce = Pointer to Device Control Entry (DCE)
  1875. //    Output:            A status code is returned.
  1876. //-------------------------------------------------------------------------------
  1877. OSStatus DoReadWriteCommand( UTDriverGlobals *theDriverPB, Boolean doWrite )
  1878. {
  1879.     OSStatus        err;
  1880.     UInt32            startingBlock, numBlocks;
  1881.     VolumeRecPtr    vol;
  1882.     IOParamPtr         iopb;
  1883.     DriveRecPtr     drive;
  1884.     
  1885.     drive = &theDriverPB->theDrive;
  1886.     iopb = (IOParamPtr) theDriverPB->drivePB.theIOPB;
  1887.  
  1888.     // Find the associated volume and drive records required for the request.
  1889.     vol = GetVolume(drive, iopb->ioVRefNum, 0);            // Get the volume record requested 
  1890.     
  1891.     //if (vol)                                        // if we have a volume
  1892.     //    drive = (DriveRecPtr)vol->drivePtr;            // get its drive record
  1893.     //else                // assume request if for a physical drive
  1894.     if (!vol)                                        // if we have a volume
  1895.     {
  1896.         if (drive)                                    // if physical drive matches…
  1897.         {
  1898.             vol = drive->nextVol;
  1899.             if (vol)                                // and a volume exists for it…
  1900.             {
  1901.                 if (vol->curoffset)                    // and not doing physical addressing…
  1902.                 {
  1903.                     vol = 0;                        // the volume is invalid
  1904.                 }
  1905.             }
  1906.         }
  1907.     }
  1908.     
  1909.     if (!vol || !drive)                // Abort if we don't have both drive and volume records
  1910.         return(nsDrvErr);
  1911.         
  1912.     //................................................................................
  1913.     // A usable drive and volume exists.  Continue processing the request…
  1914.  
  1915.     // Compute the starting block address for the request.  We accept
  1916.     // only the normal 32 bit address and not the new 'Large Volume (64 bit) Addressing'.
  1917.     //SysDebugStr("\pCalculate the Read.");
  1918.     startingBlock = (UInt32)((iopb->ioPosOffset)/(drive->blockSize));
  1919.     numBlocks = (iopb->ioReqCount)/(drive->blockSize);
  1920.  
  1921.     if (doWrite && vol->driveStatus.writeProt)
  1922.     {
  1923.         // check for write protect
  1924.         err = wPrErr;
  1925.     }
  1926.     else if (iopb->ioReqCount & ((drive->blockSize) - 1))
  1927.     {
  1928.         // Verify if request is a multiple of the drives blocksize
  1929.         err = paramErr;
  1930.     }
  1931.     else if ( ( startingBlock + numBlocks ) > ((vol->curoffset) ? vol->partblks : drive->capacity))
  1932.     {
  1933.         // Verify if request is within range with respect to doing physical or logical I/O.
  1934.         // Access is limited to partition range (logical I/O) if curoffset is non-zero.
  1935.         err = paramErr;
  1936.     }
  1937.     else if ((iopb->ioPosMode & rdVerify) && !doWrite)
  1938.     {
  1939.         // If Read-Verify mode, this is not efficient with disks so we simply pretend we did it.
  1940.         iopb->ioActCount = iopb->ioReqCount;
  1941.         iopb->ioPosOffset += iopb->ioActCount;
  1942.         err = noErr;
  1943.     } 
  1944.     else                                    // Do the read or write
  1945.     {
  1946.         startingBlock += vol->curoffset;            // add in the partition offset
  1947.         err = ReadWriteBlock( theDriverPB, startingBlock, numBlocks, iopb->ioBuffer, doWrite, true );
  1948.     }
  1949.  
  1950.     return err;
  1951. }
  1952.  
  1953. //------------------------------------------------------------------------------
  1954. //    Function:        ReadWriteBlock
  1955. //
  1956. //    Description:    Low level read/write block on the media with retries.
  1957. //
  1958. //    Input:            drive:            Pointer to physical drive record
  1959. //                        blockAddr:        Starting block address 
  1960. //                        numBlocks:        Number of blocks to read/write
  1961. //                        buffer:            Pointer to buffer
  1962. //                        doWrite:            1 = write, 0 = read
  1963. //
  1964. //    Output:            true if successful, false if not
  1965. //-------------------------------------------------------------------------------
  1966. OSStatus ReadWriteBlock( UTDriverGlobals *theDriverPB, UInt32 startBlock, UInt32 numBlocks, Ptr buffer, Boolean doWrite, Boolean doAsync)
  1967. {    
  1968.     StorageExecuteCommandPBPtr     theReadWriteRequest;
  1969.     UInt32                        driveBlockSize;
  1970.     volatile OSStatus            status = noErr;
  1971.  
  1972.     IfDebugging("\p…ReadWriteBlock");
  1973.     theDriverPB->drivePB.status = noErr;
  1974.     driveBlockSize = theDriverPB->theDrive.blockSize;
  1975.     theReadWriteRequest = &(theDriverPB->drivePB.executePB);
  1976.  
  1977.     BlockZero(theReadWriteRequest, sizeof(StorageExecuteCommandPB));    // clear out the PB we will send
  1978.     theReadWriteRequest->userBuffer = buffer;                // -> Pointer to user buffer
  1979.     theReadWriteRequest->expectedCount = numBlocks*driveBlockSize;    // -> Expected number of bytes to transfer
  1980.  
  1981.     theReadWriteRequest->completionProc = (StorageClassCompletionProcPtr) ReadWriteCompletion;    // -> Completion routine
  1982.     theReadWriteRequest->actualCount = 0;                    // <- Actual number of bytes transferred
  1983.     theReadWriteRequest->status = 0;                    // <- Result of operation
  1984.  
  1985.     if(doWrite)
  1986.     {
  1987.         theReadWriteRequest->cdb[0] = kCmdWrite;
  1988.         theReadWriteRequest->flags     = kStorageDataOut;        // -> Expect a data out transfer
  1989.     }
  1990.     else
  1991.     {
  1992.         theReadWriteRequest->cdb[0] = kCmdRead;
  1993.         theReadWriteRequest->flags     = kStorageDataIn;        // -> Expect a data in transfer
  1994.     }
  1995.     
  1996.     // Set the starting block in the CDB
  1997.     theReadWriteRequest->cdb[2] = (startBlock >> 24) & 0xff;
  1998.     theReadWriteRequest->cdb[3] = (startBlock >> 16) & 0xff;
  1999.     theReadWriteRequest->cdb[4] = (startBlock >> 8) & 0xff;
  2000.     theReadWriteRequest->cdb[5] = startBlock & 0xff;
  2001.     
  2002.     // Set the Block Count in the CDB
  2003.     theReadWriteRequest->cdb[7] = (numBlocks >> 8) & 0xff;
  2004.     theReadWriteRequest->cdb[8] = numBlocks & 0xff;
  2005.  
  2006.     status = ((gItsTheDispatchTable)->pStorageExecuteCmd)( theReadWriteRequest );            
  2007.  
  2008.     theDriverPB->drivePB.status = status;
  2009.  
  2010.     if(status != 1)
  2011.     {
  2012.         theDriverPB->drivePB.theIOPB = nil;
  2013.     }
  2014.     else
  2015.     {
  2016.         if(doAsync == false)
  2017.         {
  2018.             while ( status == 1 )
  2019.             {
  2020.                 status = theDriverPB->drivePB.status;
  2021.             }
  2022.         }
  2023.     }
  2024.     
  2025.     return status;
  2026. }
  2027.  
  2028.  
  2029. void ReadWriteCompletion( void *theDriverPB )
  2030. {
  2031.     OSStatus            status;
  2032.     UTDriverGlobals        *ourPB;
  2033.     Boolean             wasWrite;
  2034.     
  2035.     //SysDebugStr("\pReadWriteCompletion;g");
  2036.     ourPB = (UTDriverGlobals *) theDriverPB;
  2037.     status = ourPB->drivePB.executePB.status;
  2038.     wasWrite = ((ourPB->drivePB.executePB.flags & kStorageDataOut) == kStorageDataOut);
  2039.     if(ourPB->doInternalReadWrite == false)
  2040.     {
  2041.         IOParamPtr         iopb;
  2042.  
  2043.         iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2044.         
  2045.         if( status == noErr )
  2046.         {
  2047.             if( wasWrite == true )
  2048.             {
  2049.                 // It was a write, do a Request sense to determine if any 
  2050.                 // errors occurred.
  2051.                 status = RequestSense( ourPB, &WriteRequestSenseCompletion);
  2052.                 
  2053.                 if( status != 1)
  2054.                 {
  2055.                     // An error occurred while trying to do the Request sense,
  2056.                     // return an ioErr to the system.
  2057.                     iopb->ioActCount = 0;
  2058.                     
  2059.                     // Set the status in the DriverPB last, this way if there is an immediate command,
  2060.                     // It won't think the command is done till after our processing.
  2061.                     ourPB->drivePB.status = ioErr;
  2062.                     
  2063.                     // Signal completion of the command to the operating system
  2064.                     ourPB->drivePB.theIOPB = nil;
  2065.                     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2066.                 }
  2067.             }
  2068.             else
  2069.             {
  2070.                 // This was a read command, and no errors occurred, finish the IO request and
  2071.                 // return to the system.
  2072.                 iopb->ioActCount = iopb->ioReqCount;
  2073.                 
  2074.                 // Set the status in the DriverPB last, this way if there is an immediate command,
  2075.                 // It won't think the command is done till after our processing.
  2076.                 ourPB->drivePB.status = noErr;
  2077.                 
  2078.                 // Signal completion of the command to the operating system
  2079.                 ourPB->drivePB.theIOPB = nil;
  2080.                 FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2081.             }
  2082.         }
  2083.         else
  2084.         {
  2085.             // An error occurred on the command, do a Request sense to be certain any
  2086.             // USB Device stalls are cleared.
  2087.             status = RequestSense( ourPB, &RequestSenseOnErrorCompletion);
  2088.             
  2089.             if( status != 1)
  2090.             {
  2091.                 // An error occurred while trying to do the Request sense,
  2092.                 // return an ioErr to the system.
  2093.                 iopb->ioActCount = 0;
  2094.                 
  2095.                 // Set the status in the DriverPB last, this way if there is an immediate command,
  2096.                 // It won't think the command is done till after our processing.
  2097.                 ourPB->drivePB.status = ioErr;
  2098.                 
  2099.                 // Signal completion of the command to the operating system
  2100.                 ourPB->drivePB.theIOPB = nil;
  2101.                 FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2102.             }
  2103.         }
  2104.     }
  2105.     else
  2106.     {
  2107.         // If this is an internal command, this is all we care about
  2108.         ourPB->drivePB.status = status;
  2109.     }
  2110.  
  2111.     IfDebugging("\p…ReadWriteBlock Done");
  2112. }
  2113.  
  2114. void WriteRequestSenseCompletion( void *theDriverPB )
  2115. {
  2116.     OSStatus        status;
  2117.     UTDriverGlobals    *ourPB;
  2118.     IOParamPtr         iopb;
  2119.     
  2120.     ourPB = (UTDriverGlobals *) theDriverPB;
  2121.     status = ourPB->drivePB.executePB.status;
  2122.     iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2123.  
  2124.     if( status == noErr )
  2125.     {
  2126.         if( ( ourPB->sense[2] & 0x0F !=0 ) && ( ourPB->sense[2] & 0x0F !=1 ))
  2127.         {
  2128.             // An error has been reported back in the sense key, return 
  2129.             // an ioErr to the system.
  2130.             iopb->ioActCount = 0;
  2131.             ourPB->drivePB.status = ioErr;
  2132.         }
  2133.         else
  2134.         {
  2135.             // No errors has been reported back in the sense key, return 
  2136.             // a noErr to the system.
  2137.             iopb->ioActCount = iopb->ioReqCount;
  2138.             ourPB->drivePB.status = noErr;
  2139.         }
  2140.     }
  2141.     else
  2142.     {
  2143.         // Errors occurred on the Request sense report an
  2144.         // ioErr back to the system
  2145.         iopb->ioActCount = 0;
  2146.         ourPB->drivePB.status = ioErr;
  2147.     }
  2148.  
  2149.     // Signal completion of the command to the operating system
  2150.     //SysDebugStr("\pWriteReqSenseFinishCommand");
  2151.     ourPB->drivePB.theIOPB = nil;
  2152.     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2153. }
  2154.  
  2155. void RequestSenseOnErrorCompletion( void *theDriverPB )
  2156. {
  2157.     UTDriverGlobals    *ourPB;
  2158.     IOParamPtr         iopb;
  2159.     
  2160.     ourPB = (UTDriverGlobals *) theDriverPB;
  2161.     iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2162.         
  2163.     // An error occurred on the initial command, and no error occurred on the RequestSense
  2164.     if( (ourPB->drivePB.retryCount < kReadWriteRetryCount) && (ourPB->drivePB.executePB.status == noErr))
  2165.     {
  2166.         OSStatus err;
  2167.         // We have not yet exceeded the retry count, so try the operation again
  2168.             // Increment our retry counter
  2169.             ourPB->drivePB.retryCount += 1;
  2170.     
  2171.             // Send the command out again
  2172.             err = DoReadWriteCommand( ourPB, ourPB->drivePB.doWrite);
  2173.         
  2174.         if ( err !=1 )
  2175.         {
  2176.             iopb->ioActCount = 0;
  2177.             ourPB->drivePB.status = ioErr;
  2178.         
  2179.             // Signal completion of the command to the operating system
  2180.             ourPB->drivePB.theIOPB = nil;
  2181.             FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2182.         }
  2183.     }
  2184.     else
  2185.     {
  2186.         // Since we only get here if an error occurred on the orignal
  2187.         // command, and the Request Sense was to clear any device stalls.
  2188.         // We will always report back an ioErr to the system.
  2189.         iopb->ioActCount = 0;
  2190.         ourPB->drivePB.status = ioErr;
  2191.     
  2192.         // Signal completion of the command to the operating system
  2193.         ourPB->drivePB.theIOPB = nil;
  2194.         FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2195.     }
  2196. }
  2197.  
  2198.  
  2199. //------------------------------------------------------------------------------
  2200. //    Function:        GetVolume
  2201. //
  2202. //    Description:    Searches for the volume record with the specified vRefNum and 
  2203. //                    returns its VolumeRecPtr.  Optionally, partitionNum
  2204. //                    can be use as search keys if vRefNum is zero.
  2205. //
  2206. //    Input:            drive:            the pointer to the DriveRec
  2207. //                    vRefNum:        the volume's reference number
  2208. //                    partitionNum:    the volume's partition number
  2209. //
  2210. //    Output:            pointer to volume record if found, nil if not found
  2211. //-------------------------------------------------------------------------------
  2212. VolumeRecPtr GetVolume(DriveRecPtr drive, UInt16 vRefNum, UInt32 partitionNum)
  2213. {
  2214.     VolumeRecPtr vol = nil;
  2215.     Boolean found;
  2216.     
  2217.     // Search all volume records for one matching the search key
  2218.  
  2219.     vol = drive->nextVol;                        // first volume pointer
  2220.  
  2221.     while (vol)
  2222.     {
  2223.         found = (vRefNum) ? (vol->vRefNum == vRefNum) : (vol->partitionNo == partitionNum);
  2224.  
  2225.         if (found)
  2226.             return(vol);                        // found the partition
  2227.         else
  2228.             vol = (VolumeRecPtr)vol->nextVol;    // otherwise, get next volume pointer
  2229.     }
  2230.     
  2231.     return(vol);                                // nil if volume not found
  2232. }
  2233.  
  2234.  
  2235. //------------------------------------------------------------------------------
  2236. //    Function:        CreateVolume
  2237. //
  2238. //    Description:    This function creates a volume record for the specified drive.
  2239. //                        The volume is appended to the drive's volume queue and a logical
  2240. //                        logical drive is installed in the system drive queue.  It is 
  2241. //                        assumed the volume is not write protected, drive is installed,
  2242. //                        and the media is installed.  
  2243. //                    
  2244. //    Input:            drive:            pointer to the drive record of the volume to create
  2245. //                    partitionID:    the volume's partition ID
  2246. //                    volSize:        size of the volume in blocks
  2247. //                    volOffset:        block offset of volume on drive
  2248. //
  2249. //    Output:            Returns nil pointer if fails, else pointer to volume record
  2250. //
  2251. //    NOTE:            Assumes all inputs are valid!
  2252. //-------------------------------------------------------------------------------
  2253. VolumeRecPtr CreateVolume(DriveRecPtr drive, UInt32 partitionID, UInt32 volSize, UInt32 volOffset )
  2254. {
  2255.     register VolumeRecPtr        vol;
  2256.     VolumeRecPtr*                volHandle;
  2257.     Boolean                        volInQueue = false;
  2258.     
  2259.     // When a volume is offlined by the system (greyed desktop icon) it is still
  2260.     // being used and its associated DrvQEl must remain.  Thus, its volume and 
  2261.     // physical drive record also must remain.  So, when new media is inserted 
  2262.     // we need to check for these structures and reuse them before creating new ones.
  2263.     // The structures can be reused since the file system verifies the volume.
  2264.  
  2265.     if( drive->nextVol == nil)
  2266.     {
  2267.         // Make sure the drive's volume queue points to the volume
  2268.         vol = &gTheUTGlobals.theVolume;
  2269.     }
  2270.     else
  2271.     {
  2272.         // Search for a volume record for the volume (partition) to be created…
  2273.         vol = GetVolume(drive, 0, partitionID);
  2274.         
  2275.         if (vol)                                                        // if record exists for this volume…
  2276.         {
  2277.             volInQueue = true;                                            // remember volume is already in queue
  2278.     
  2279.             if (vol->driveStatus.diskInPlace)                            // if volume is already online…
  2280.                 vol = nil;                                                // we shouldn't be here - fall thru
  2281.         }
  2282.         else                                                            // need to create volume record
  2283.         {
  2284.             vol = (VolumeRecPtr) PoolAllocateResident(sizeof(VolumeRec), true);    // Allocate storage for record
  2285.         }
  2286.     }
  2287.     
  2288.     if (vol)                                                        // if a volume record is valid…
  2289.     {    
  2290.         if( gTheUTGlobals.isFloppy == true)
  2291.         {
  2292.             vol->driveStatus.track             = 80;            // Sectors on a MFM disk
  2293.             vol->driveStatus.sides             = -1;            // -1 means double sided floppy
  2294.         }
  2295.         else
  2296.         {
  2297.             vol->driveStatus.track             = 0;            // Only used on floppys
  2298.             vol->driveStatus.sides             = 0;            // Only used on floppys
  2299.         }        
  2300.  
  2301.         if( gTheUTGlobals.isWriteProtected == true)
  2302.         {
  2303.             vol->driveStatus.writeProt     = 0x80;            // disk is write protected
  2304.         }
  2305.         else
  2306.         {
  2307.             vol->driveStatus.writeProt     = 0;            // not write protected yet
  2308.         }
  2309.         vol->driveStatus.diskInPlace     = 1;            // Ejectable Disk
  2310.         vol->driveStatus.installed         = 1;            // drive is installed
  2311.         vol->driveStatus.dQFSID         = 0;                        // File Manager's volume type
  2312.         if( gTheUTGlobals.isFloppy == true)
  2313.         {
  2314.             DrvSts    *theDriveStatus;
  2315.             
  2316.             theDriveStatus = (DrvSts *) &vol->driveStatus;
  2317.             theDriveStatus->qType         = 0;                        // This is a floppy drive status structure
  2318.             theDriveStatus->twoSideFmt = -1;                        // after 1st rd/wrt: 0=1 side, -1=2 side
  2319.             theDriveStatus->needsFlush = -1;                        // -1 for MacPlus drive
  2320.             theDriveStatus->diskErrs = 0;                            // soft error count
  2321.  
  2322.             vol->mediaIconPtr =    (Ptr) AppleFloppyMediaIcon;        // media icon for floppies
  2323.         }
  2324.         else
  2325.         {
  2326.             vol->driveStatus.qType             = 1;                    // both dQDrvSz and dQDrvSz2 are used
  2327.             vol->driveStatus.driveSize        = (UInt16) volSize;        // volume size in blocks
  2328.             vol->driveStatus.driveS1         = (UInt16) (volSize >> 16);
  2329.             
  2330.             vol->mediaIconPtr =    (Ptr) &CartridgeIcon;                    // media icon for cartridge
  2331.         }
  2332.             
  2333.  
  2334.         vol->mountthispart =    false;                                // don't mount this volume,
  2335.         vol->partmounted =        false;                                // it's not mounted yet,
  2336.         vol->partitionNo =        partitionID;                        // save the partition ID
  2337.  
  2338.         // Save volume's block offset and set its access mode by setting curoffset to the
  2339.         // same (access is relative to partition offset if curoffset is non-zero, else physical)
  2340.         vol->curoffset =        volOffset;                            // block offset of volume
  2341.         vol->partoffset =        volOffset;                            // partition offset
  2342.         vol->partblks =            volSize;                            // save size for our use also
  2343.         vol->drivePtr =        (Ptr) drive;                            // pointer to vol's physical drive
  2344.         // If this record is not in our volume list yet, finish initializing and insert
  2345.         if (!volInQueue)                                            // If not in the volume queue…
  2346.         {
  2347.             vol->driveStatus.qLink = nil;                            // initialize system queue link
  2348.             vol->nextVol = nil;                                        // no link to next volume yet
  2349.  
  2350.             // Find the end of the drive's volume queue and link in the new volume record.
  2351.             volHandle = &(drive->nextVol);                            // point to start of drive's volume queue
  2352.             while (*volHandle)                                        // search for end of volume queue…
  2353.             {
  2354.                 volHandle = (VolumeRecPtr*)&((*volHandle)->nextVol);
  2355.             }
  2356.             
  2357.             *volHandle = vol;                                        // at end of queue, insert volume
  2358.         }
  2359.  
  2360.         drive->numVolumes++;                                        // update number of drive volumes
  2361.     }
  2362.     return(vol);
  2363. }
  2364.  
  2365.  
  2366. //------------------------------------------------------------------------------
  2367. //    Function:        InstallVolumes
  2368. //    Description:    Searches for partitions on the media and installs them as
  2369. //                        volumes for the associated drive.  Assumes the drive does
  2370. //                        not have any volumes installed yet. 
  2371. //
  2372. //    Input:            theDrive:    pointer to drive record
  2373. //                        mountVols:    true means to mount the drive's partitions
  2374. //
  2375. //-------------------------------------------------------------------------------
  2376. void InstallVolumes(DriveRecPtr theDrive, Boolean mountVols)
  2377. {
  2378.     VolumeRecPtr    vol = nil;
  2379.  
  2380.     // If the driver is going to support partitions, this is were the media should be 
  2381.     // scanned to see if there are any valid HFS or DOS partitions.
  2382.     
  2383.     // If no valid paritions are found, or partitions are not support, the following code
  2384.     // will create a volume of the entire media capacity, post a disk inserted
  2385.     // event and let the File System Manager try and figure it out.  Note we post a disk
  2386.     // inserted event rather than notifying FSM so if the media is not recognized (because
  2387.     // it's unformatted or the correct file system is not installed) the system will prompt
  2388.     // with a "This is not a Macintosh disk…" message.  If we call FSM instead, the user will
  2389.     // not be prompted when the media is unformatted.  This provides a way to format media.
  2390.  
  2391.     if (theDrive->numVolumes == 0)                            // if no partitions were found, or are not supported
  2392.     {
  2393.         IfDebugging("\ptheDrive->numVolumes == 0");
  2394.         vol = CreateVolume(theDrive, 1, theDrive->capacity, 0);
  2395.         if (vol)
  2396.         {
  2397.             vol->mountthispart = true;                                    // post disk inserted event later
  2398.         }
  2399.     }
  2400.  
  2401.     // Add the remaining volumes to the drive queue…
  2402.     vol = theDrive->nextVol;                                            // first volume of the drive
  2403.     while (vol)
  2404.     {
  2405.         if ((vol->mountthispart == true) || (mountVols == false))
  2406.         {
  2407.             UInt16 volNumber;
  2408.             
  2409.             vol->vRefNum = NextQDrive();        // assign a logical drive number
  2410.             volNumber = vol->vRefNum;
  2411.             
  2412.             NativeAddDrive(gTheUTGlobals.drvrRefNum, volNumber,(DrvQElPtr)  &vol->driveStatus.qLink);
  2413.         }
  2414.         
  2415.         vol = (VolumeRecPtr)vol->nextVol;                            // point to the next volume
  2416.     }
  2417. }
  2418.  
  2419.  
  2420. //------------------------------------------------------------------------------
  2421. //    Function:        RemoveVolume
  2422. //    Description:    Deletes a volume and its record
  2423. //    Input:            drvRec:        pointer to physical drive record of volume
  2424. //                    volRef:        Volume reference
  2425. //-------------------------------------------------------------------------------
  2426. void RemoveVolume(DriveRecPtr drvRec, UInt16 volRef)
  2427. {
  2428.     VolumeRecPtr*    pvHandle = &(drvRec->nextVol);
  2429.     VolumeRecPtr    pvPtr = *pvHandle;
  2430.     
  2431.     while (pvPtr) 
  2432.     {
  2433.         if (pvPtr->vRefNum == volRef)             // found the volume record
  2434.         {        
  2435.             Dequeue((QElemPtr) &(pvPtr->driveStatus.qLink), GetDrvQHdr());    // remove from drive queue        
  2436.             *pvHandle = (VolumeRecPtr)(pvPtr->nextVol);    // remove it from the linked list
  2437.             if (pvPtr == &gTheUTGlobals.theVolume)
  2438.             {
  2439.                 BlockZero((Ptr) &gTheUTGlobals.theVolume, sizeof(VolumeRec));
  2440.             }
  2441.             else
  2442.             {
  2443.                 PoolDeallocate( pvPtr );            // release its memory
  2444.             }
  2445.             drvRec->numVolumes--;                // decrement number of drive volumes
  2446.             break;
  2447.         }
  2448.         
  2449.         pvHandle = (VolumeRecPtr*)&(pvPtr->nextVol);        // save pointer to pv pointer
  2450.         pvPtr = (VolumeRecPtr)pvPtr->nextVol;                // point to next pv
  2451.     }            
  2452. }
  2453.  
  2454.  
  2455. //------------------------------------------------------------------------------
  2456. //    Function:        InstallDrive
  2457. //
  2458. //    Description:    Installs a physical drive and its volumes under the driver's
  2459. //                        control.  The driver determines if the drive is one it can
  2460. //                        manage, and if so, creates and initializes the drive's record,
  2461. //                        sets the drives operating mode and options, and mounts its
  2462. //                        partitions to the system.
  2463. //
  2464. //    Input:            drvNum:        physical drive reference
  2465. //                        mountVols:    true means to mount drive's partitions
  2466. //
  2467. //    Output:            true if successful, false if not
  2468. //-------------------------------------------------------------------------------
  2469. Boolean InstallDrive(DriveRecPtr theDrive, Boolean mountVols)
  2470. {
  2471.     // Initialize variables associated with the new drive.
  2472.     theDrive->numVolumes =        0;                            // no partitions yet
  2473.     
  2474.     //..............................................................................
  2475.     // Search for file system partitions on the media and install them as volumes
  2476.     // of this drive.  If no volumes, the drive must be considered unusable.
  2477.     InstallVolumes(theDrive, mountVols);
  2478.  
  2479.     if (theDrive->numVolumes)
  2480.     {
  2481.         OSErr theErr;
  2482.         
  2483.         theErr = MountVolumes( theDrive );
  2484.         if(theErr != noErr)
  2485.         {
  2486.             // For some reason, the disk could not be mounted,
  2487.             // We should eject and let the user decide whether to try again.
  2488.             gTheUTGlobals.currentExecutionState = kEjectStartState;
  2489.             EjectTheCartridge( &gTheUTGlobals );
  2490.         }
  2491.     }
  2492.     else                                                    // Abort if no volumes installed for drive        
  2493.     {
  2494.         return(false);
  2495.     }
  2496.  
  2497.     return(true);
  2498. }
  2499.  
  2500.  
  2501. //------------------------------------------------------------------------------
  2502. //    Function:        RemoveDrive
  2503. //    Description:    Removes a physical drive and its volumes from our control
  2504. //                    
  2505. //-------------------------------------------------------------------------------
  2506. void RemoveDrive(DriveRecPtr theDrivePtr)
  2507. {        
  2508.     VolumeRecPtr    vol;
  2509.     UInt16            vrefnum;
  2510.     
  2511.     if (theDrivePtr)                                // If the drive exists…
  2512.     {
  2513.         while (theDrivePtr->nextVol)                // dequeue all volumes on the drive…
  2514.         {
  2515.             vol = theDrivePtr->nextVol;                // get volume
  2516.             vrefnum = vol->vRefNum;                    // get its refnum
  2517.             RemoveVolume(theDrivePtr, vrefnum);        // delete volume from our queue
  2518.         }
  2519.     }
  2520. }
  2521.  
  2522.  
  2523. //------------------------------------------------------------------------------
  2524. //    Function:        NextQDrive
  2525. //
  2526. //    Description:    Returns the next unused logical drive number from the system 
  2527. //                    Drive Queue.  The Drive Queue is searched starting with a
  2528. //                    logical drive number 8.
  2529. //                    
  2530. //    Input:            none
  2531. //
  2532. //    Output:            The highest Drive Queue drive number + 1
  2533. //-------------------------------------------------------------------------------
  2534. SInt16 NextQDrive( void )
  2535. {
  2536.     QHdrPtr    qhdr = GetDrvQHdr();                // Pointer to Drive Queue 
  2537.     DrvQEl    *qel = (DrvQEl*) (qhdr->qHead);        // Pointer to first element 
  2538.     SInt16    drv = 8;                            // Start above built in drives 
  2539.  
  2540.     while (qel)                                 // While not end of queue, 
  2541.     {
  2542.         if (qel->dQDrive == drv)                 // if drive number used, 
  2543.         {
  2544.             drv++;                                // bump number, and 
  2545.             qel = (DrvQEl*) (qhdr->qHead);        // search from start 
  2546.         }
  2547.         else                                    // else next queue element 
  2548.             qel = (DrvQEl*) (qel->qLink);
  2549.     }
  2550.  
  2551.     return(drv);                        // Return the next logical drive 
  2552. }
  2553.  
  2554.  
  2555. //------------------------------------------------------------------------------
  2556. //    Function:        FindMountedVol
  2557. //    Description:    Searches the volume queue for a mounted volume specified 
  2558. //                    by vRefNum.  If one is found, its VCB pointer is returned.
  2559. //                    
  2560. //    Input:            vRefNum:    the volume reference to search for
  2561. //
  2562. //    Output:            the VCB pointer (NULL = volume not mounted)
  2563. //-------------------------------------------------------------------------------
  2564. static VCB *FindMountedVol(SInt16 vRefNum)
  2565. {
  2566.     QHdrPtr    volQ = GetVCBQHdr();        // VCB queue head pointer
  2567.     VCB        *theVol = (volQ) ? (VCB*)volQ->qHead : 0;    // first VCB
  2568.  
  2569.     while(theVol)
  2570.     {
  2571.         // The test for whether a volume is mounted or not is done using
  2572.         // the VCB fields vcbDrvNum and vcbDRefNum.  A volume is mounted
  2573.         // only if it is online.
  2574.         //
  2575.         //                    online            offline            ejected
  2576.         //
  2577.         //    vcbDrvNum        >0 (DrvNum)         0                 0
  2578.         //    vcbDRefNum        <0 (DRefNum)    <0 (-DrvNum)    >0 (DrvNum)
  2579.     
  2580.         if (theVol->vcbDrvNum == vRefNum)    // if volume specified is online…
  2581.             break;                            // volume is mounted
  2582.  
  2583.         theVol = (VCB*)theVol->qLink;        // next VCB
  2584.     }
  2585.     
  2586.     return(theVol);
  2587. }
  2588.  
  2589. //------------------------------------------------------------------------------
  2590. //    Function:        MountedVolOfDrive
  2591. //
  2592. //    Description:    Searches the system VCB for a mounted volume on the specified 
  2593. //                    physical drive.
  2594. //                    
  2595. //    Input:            drive:    pointer to the physical drive's record
  2596. //
  2597. //    Output:            the VCB pointer (nil if no volume mounted for drive)
  2598. //-------------------------------------------------------------------------------
  2599. VCB * MountedVolOfDrive(DriveRecPtr drive)
  2600. {
  2601.     VolumeRecPtr    vol = nil;
  2602.     VCB                *vcb = nil;
  2603.     
  2604.     if (drive)
  2605.     {
  2606.         vol = drive->nextVol;                // first volume (logical drive) of drive
  2607.         while (vol)
  2608.         {
  2609.             vcb = FindMountedVol(vol->vRefNum);        // check if a volume is mounted
  2610.             if (vcb)                                // if mounted volume, stop now
  2611.             {
  2612.                 break;
  2613.             }
  2614.             else                                                // next volume of drive
  2615.             {
  2616.                 vol = (VolumeRecPtr)vol->nextVol;
  2617.         }
  2618.     }
  2619.     }
  2620.     
  2621.     return(vcb);
  2622. }
  2623.  
  2624.  
  2625. //------------------------------------------------------------------------------
  2626. //    Function:        UpdateQ
  2627. //    Description:    Updates the specified drive in the system drive queue with
  2628. //                    the specified capacity
  2629. //                    
  2630. //    Input:            qDrive:        the drive to update
  2631. //                    newSize:    the new drive capacity
  2632. //-------------------------------------------------------------------------------
  2633. void UpdateQ(SInt16 qDrive, SInt32 newSize)
  2634. {
  2635.     QHdrPtr qhdr = GetDrvQHdr();                // Pointer to Drive Queue 
  2636.     DrvQEl *qel = (DrvQEl*) (qhdr->qHead);        // Pointer to first element 
  2637.  
  2638.     while (qel) 
  2639.     {
  2640.         // Search until drive is found, then update its capacity
  2641.         if (qel->dQDrive == qDrive)                // if drive number found, 
  2642.         {        
  2643.             qel->dQDrvSz2 = *(UInt16*)&newSize;    // new capacity (hi word)
  2644.             qel->dQDrvSz = newSize;                        // low word of capacity 
  2645.             break;
  2646.         }
  2647.         else                            // else next queue element 
  2648.             qel = (DrvQEl*) (qel->qLink);
  2649.     }
  2650. }
  2651.  
  2652. //------------------------------------------------------------------------------
  2653. //    FUNCTION:    NextPartitionID
  2654. //    PURPOSE:    Returns the next unique partition ID for all volumes associated
  2655. //                with the specified drive.  NOTE: This function should be used only
  2656. //                when adding volumes which do not have a partition map on the media.
  2657. //                
  2658. //    INPUT:        drive:    pointer to the drive record to search for next partition ID
  2659. //
  2660. //    OUTPUT:        a unique partition ID related to the specified volume
  2661. //
  2662. //-------------------------------------------------------------------------------
  2663. SInt32 NextPartitionID(DriveRecPtr drive)
  2664. {
  2665.     VolumeRecPtr vol = 0;
  2666.     SInt32 nextPartitionID = 1;                // Start search with partition ID of 1
  2667.  
  2668.     if (drive)                                // if drive is present…
  2669.     {
  2670.         vol = (VolumeRecPtr)drive->nextVol;            // first volume pointer
  2671.  
  2672.         while (vol)                            // while not end of volume queue…
  2673.         {
  2674.             if (vol->partitionNo == nextPartitionID)    // if partition ID used
  2675.             {
  2676.                 nextPartitionID++;                    // bump partition ID
  2677.                 vol = drive->nextVol;                // reset volume pointer
  2678.             }
  2679.             else
  2680.                 vol = (VolumeRecPtr)vol->nextVol;            // otherwise, point to next volume
  2681.         }
  2682.     }
  2683.     
  2684.     return(nextPartitionID);            // nil if volume not found
  2685. }
  2686.  
  2687.  
  2688. //------------------------------------------------------------------------------
  2689. //    Function:        MountVolumes
  2690. //    Description:    Mounts all volume for the specified drive.
  2691. //
  2692. //    Input:            DriveRecPtr drive:    drive with volumes to mount
  2693. //
  2694. //    Output:            Returns any errors that occur from PostEvent
  2695. //-------------------------------------------------------------------------------
  2696. OSErr MountVolumes( DriveRecPtr drive )
  2697. {
  2698.     VolumeRecPtr        vol;
  2699.     OSErr                mountErr = noErr;
  2700.                 
  2701.     // Post a Disk Inserted event for all HFS volumes not yet mounted
  2702.     if (drive)                                                        // for each drive…
  2703.     {
  2704.         vol = drive->nextVol;                                        // first volume structure
  2705.         while (vol)                                                 // for all volumes on drive…
  2706.         {                            
  2707.             if ((vol->driveStatus.diskInPlace != 0) &&                // if media in place,
  2708.                (vol->mountthispart) &&                                 // and volume to be mounted,
  2709.                (!vol->partmounted))                                    // and hasn't been done yet
  2710.             {
  2711.                 mountErr = PostEvent(diskEvt, vol->vRefNum);
  2712.  
  2713.                 if ( mountErr == noErr )
  2714.                 {
  2715.                     vol->partmounted = true;
  2716.                 }
  2717.             }
  2718.  
  2719.             vol = (VolumeRecPtr)vol->nextVol;                        // next per volume pointer
  2720.         }
  2721.     }
  2722.  
  2723.     return mountErr;                                                // return error if one occurred
  2724. }
  2725.  
  2726.  
  2727. // This is to workaround a bug in the PowerPC native version of the AddDrive
  2728. // call in systems before 8.5, where one needs to be added to the desired
  2729. // drive number before calling AddDrive.
  2730. // This function will check the system and pass the appropriate value to AddDrive
  2731. void NativeAddDrive(SInt16 drvrRefNum, UInt16 driveNumber, DrvQElPtr drvQEl)
  2732. {
  2733.     UInt32        gestaltResponse;
  2734.  
  2735.     // Check System version to see if we need to add one to AddDrive calls
  2736.     Gestalt    (gestaltSystemVersion,(long *) &gestaltResponse);
  2737.     if( (gestaltResponse&0xFFFF) < 0x0850 )
  2738.     {
  2739.         // We are on a system before 8.5, we need to add 1 to AddDrive calls
  2740.         driveNumber += 1;
  2741.     }
  2742.  
  2743.     AddDrive( drvrRefNum, driveNumber, drvQEl);
  2744. }
  2745.  
  2746.